commit c4e1edfa8008170fe6d1988ed6f6986a1852f802 Author: Martin Fitzpatrick Date: Fri Feb 9 23:10:03 2018 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34613a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,66 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +.idea +*~ +.DS_Store +Thumbs.db +build/* +dist diff --git a/README.md b/README.md new file mode 100644 index 0000000..19dd986 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# Minute Apps + +A collection of minute (small) desktop applications written in Python +using the PyQt framework. These apps are intended as examples from +which you can poke, hack and prod your way to writing your own tools. + +If you find these apps interesting, or want to learn more about +PyQt in general, [take a look at my ebook & online course +"Create Simple GUI Applications"](https://martinfitzpatrick.name/create-simple-gui-applications) +which covers everything you need to know to get you programming with PyQt. + +Write-ups of these apps, including design explanations and walkthroughs, can be found [on my site](). + +All code is **licensed under an MIT license**. This allows you to re-use the code freely, +remixed in both commercial and non-commercial projects. The only requirement is that you must +include the MIT license file when distributing. + +## Completed + +1. Web Browser (untabbed) - "MooseAche" +1. Web Browser (tabbed) - "Mozzarella Ashbadger" +1. Minesweeper - "Moonsweeper" +1. Notepad - "No2Pads" +1. Calculator - "Calculon" (QtDesigner) +1. Word Processor - "Megasolid Idiom" +1. Webcam/Snapshot - "NSAViewer" +1. Media Player - "Failamp" +1. Post-it Notes - "Brown Note" (QtDesigner) + +## In progress + +1. Paint - "Piecasso" (QtDesigner) +1. Translator - "Babelvis" (QtDesigner) + +## Up next + +1. Solitaire - "Ronery" +1. Unzip - "7Pez" (QtDesigner) +1. Address Book - "PeepKeep" (QtDesigner) +1. RSS Reader + +## Maybe oneday + +1. Photo Gallery - "PeepPeek" +1. Vector Graphics - "Madcap Artisan" +1. Email - "GeeMail" +1. IRC - "kIRC" +1. Sound Editor +1. (Audio) Spectrum Analyzer +1. IM (Jabber) +1. FTP +1. Weather +1. Finance/Stocks +1. Process Explorer +1. Internet Radio diff --git a/browser/browser.py b/browser/browser.py new file mode 100644 index 0000000..1c18536 --- /dev/null +++ b/browser/browser.py @@ -0,0 +1,202 @@ +from PyQt5.QtCore import * +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * +from PyQt5.QtWebEngineWidgets import * +from PyQt5.QtPrintSupport import * + +import os +import sys + + +class AboutDialog(QDialog): + def __init__(self, *args, **kwargs): + super(AboutDialog, self).__init__(*args, **kwargs) + + QBtn = QDialogButtonBox.Ok # No cancel + self.buttonBox = QDialogButtonBox(QBtn) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + layout = QVBoxLayout() + + title = QLabel("MooseAche") + font = title.font() + font.setPointSize(20) + title.setFont(font) + + layout.addWidget(title) + + logo = QLabel() + logo.setPixmap(QPixmap(os.path.join('images', 'ma-icon-128.png'))) + layout.addWidget(logo) + + layout.addWidget(QLabel("Version 23.35.211.233232")) + layout.addWidget(QLabel("Copyright 2015 MooseAche Inc.")) + + for i in range(0, layout.count()): + layout.itemAt(i).setAlignment(Qt.AlignHCenter) + + layout.addWidget(self.buttonBox) + + self.setLayout(layout) + + +class MainWindow(QMainWindow): + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + self.browser = QWebEngineView() + self.browser.setUrl(QUrl("http://google.com")) + + self.browser.urlChanged.connect(self.update_urlbar) + self.browser.loadFinished.connect(self.update_title) + self.setCentralWidget(self.browser) + + self.status = QStatusBar() + self.setStatusBar(self.status) + + navtb = QToolBar("Navigation") + navtb.setIconSize(QSize(16, 16)) + self.addToolBar(navtb) + + back_btn = QAction(QIcon(os.path.join('images', 'arrow-180.png')), "Back", self) + back_btn.setStatusTip("Back to previous page") + back_btn.triggered.connect(self.browser.back) + navtb.addAction(back_btn) + + next_btn = QAction(QIcon(os.path.join('images', 'arrow-000.png')), "Forward", self) + next_btn.setStatusTip("Forward to next page") + next_btn.triggered.connect(self.browser.forward) + navtb.addAction(next_btn) + + reload_btn = QAction(QIcon(os.path.join('images', 'arrow-circle-315.png')), "Reload", self) + reload_btn.setStatusTip("Reload page") + reload_btn.triggered.connect(self.browser.reload) + navtb.addAction(reload_btn) + + home_btn = QAction(QIcon(os.path.join('images', 'home.png')), "Home", self) + home_btn.setStatusTip("Go home") + home_btn.triggered.connect(self.navigate_home) + navtb.addAction(home_btn) + + navtb.addSeparator() + + self.httpsicon = QLabel() # Yes, really! + self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-nossl.png'))) + navtb.addWidget(self.httpsicon) + + self.urlbar = QLineEdit() + self.urlbar.returnPressed.connect(self.navigate_to_url) + navtb.addWidget(self.urlbar) + + stop_btn = QAction(QIcon(os.path.join('images', 'cross-circle.png')), "Stop", self) + stop_btn.setStatusTip("Stop loading current page") + stop_btn.triggered.connect(self.browser.stop) + navtb.addAction(stop_btn) + + # Uncomment to disable native menubar on Mac + # self.menuBar().setNativeMenuBar(False) + + file_menu = self.menuBar().addMenu("&File") + + open_file_action = QAction(QIcon(os.path.join('images', 'disk--arrow.png')), "Open file...", self) + open_file_action.setStatusTip("Open from file") + open_file_action.triggered.connect(self.open_file) + file_menu.addAction(open_file_action) + + save_file_action = QAction(QIcon(os.path.join('images', 'disk--pencil.png')), "Save Page As...", self) + save_file_action.setStatusTip("Save current page to file") + save_file_action.triggered.connect(self.save_file) + file_menu.addAction(save_file_action) + + print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) + print_action.setStatusTip("Print current page") + print_action.triggered.connect(self.print_page) + file_menu.addAction(print_action) + + help_menu = self.menuBar().addMenu("&Help") + + about_action = QAction(QIcon(os.path.join('images', 'question.png')), "About MooseAche", self) + about_action.setStatusTip("Find out more about MooseAche") # Hungry! + about_action.triggered.connect(self.about) + help_menu.addAction(about_action) + + navigate_mozarella_action = QAction(QIcon(os.path.join('images', 'lifebuoy.png')), "MooseAche Homepage", self) + navigate_mozarella_action.setStatusTip("Go to MooseAche Homepage") + navigate_mozarella_action.triggered.connect(self.navigate_mozarella) + help_menu.addAction(navigate_mozarella_action) + + self.show() + + self.setWindowIcon(QIcon(os.path.join('images', 'ma-icon-64.png'))) + + def update_title(self): + title = self.browser.page().title() + self.setWindowTitle("%s - MooseAche" % title) + + def navigate_mozarella(self): + self.browser.setUrl(QUrl("https://www.udemy.com/522076")) + + def about(self): + dlg = AboutDialog() + dlg.exec_() + + def open_file(self): + filename, _ = QFileDialog.getOpenFileName(self, "Open file", "", + "Hypertext Markup Language (*.htm *.html);;" + "All files (*.*)") + + if filename: + with open(filename, 'r') as f: + html = f.read() + + self.browser.setHtml(html) + self.urlbar.setText(filename) + + def save_file(self): + filename, _ = QFileDialog.getSaveFileName(self, "Save Page As", "", + "Hypertext Markup Language (*.htm *html);;" + "All files (*.*)") + + if filename: + html = self.browser.page().mainFrame().toHtml() + with open(filename, 'w') as f: + f.write(html) + + def print_page(self): + dlg = QPrintPreviewDialog() + dlg.paintRequested.connect(self.browser.print_) + dlg.exec_() + + def navigate_home(self): + self.browser.setUrl(QUrl("http://www.google.com")) + + def navigate_to_url(self): # Does not receive the Url + q = QUrl(self.urlbar.text()) + if q.scheme() == "": + q.setScheme("http") + + self.browser.setUrl(q) + + def update_urlbar(self, q): + + if q.scheme() == 'https': + # Secure padlock icon + self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-ssl.png'))) + + else: + # Insecure padlock icon + self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-nossl.png'))) + + self.urlbar.setText(q.toString()) + self.urlbar.setCursorPosition(0) + + +app = QApplication(sys.argv) +app.setApplicationName("MooseAche") +app.setOrganizationName("MooseAche") +app.setOrganizationDomain("MooseAche.org") + +window = MainWindow() + +app.exec_() diff --git a/browser/images/arrow-000.png b/browser/images/arrow-000.png new file mode 100644 index 0000000..b39a43b Binary files /dev/null and b/browser/images/arrow-000.png differ diff --git a/browser/images/arrow-180.png b/browser/images/arrow-180.png new file mode 100755 index 0000000..fc13555 Binary files /dev/null and b/browser/images/arrow-180.png differ diff --git a/browser/images/arrow-circle-315.png b/browser/images/arrow-circle-315.png new file mode 100755 index 0000000..8baf084 Binary files /dev/null and b/browser/images/arrow-circle-315.png differ diff --git a/browser/images/cross-circle.png b/browser/images/cross-circle.png new file mode 100755 index 0000000..20d6f5e Binary files /dev/null and b/browser/images/cross-circle.png differ diff --git a/browser/images/cross.png b/browser/images/cross.png new file mode 100755 index 0000000..6b3d517 Binary files /dev/null and b/browser/images/cross.png differ diff --git a/browser/images/disk--arrow.png b/browser/images/disk--arrow.png new file mode 100755 index 0000000..bd58103 Binary files /dev/null and b/browser/images/disk--arrow.png differ diff --git a/browser/images/disk--pencil.png b/browser/images/disk--pencil.png new file mode 100755 index 0000000..47bf953 Binary files /dev/null and b/browser/images/disk--pencil.png differ diff --git a/browser/images/home.png b/browser/images/home.png new file mode 100755 index 0000000..622a2b7 Binary files /dev/null and b/browser/images/home.png differ diff --git a/browser/images/lifebuoy.png b/browser/images/lifebuoy.png new file mode 100755 index 0000000..2fb98bf Binary files /dev/null and b/browser/images/lifebuoy.png differ diff --git a/browser/images/lock-nossl.png b/browser/images/lock-nossl.png new file mode 100755 index 0000000..006528c Binary files /dev/null and b/browser/images/lock-nossl.png differ diff --git a/browser/images/lock-ssl.png b/browser/images/lock-ssl.png new file mode 100755 index 0000000..34b7748 Binary files /dev/null and b/browser/images/lock-ssl.png differ diff --git a/browser/images/ma-icon-128.png b/browser/images/ma-icon-128.png new file mode 100644 index 0000000..bd04bd3 Binary files /dev/null and b/browser/images/ma-icon-128.png differ diff --git a/browser/images/ma-icon-256.png b/browser/images/ma-icon-256.png new file mode 100644 index 0000000..d986c67 Binary files /dev/null and b/browser/images/ma-icon-256.png differ diff --git a/browser/images/ma-icon-64.png b/browser/images/ma-icon-64.png new file mode 100644 index 0000000..884fcb7 Binary files /dev/null and b/browser/images/ma-icon-64.png differ diff --git a/browser/images/printer.png b/browser/images/printer.png new file mode 100755 index 0000000..9edfbbc Binary files /dev/null and b/browser/images/printer.png differ diff --git a/browser/images/question.png b/browser/images/question.png new file mode 100755 index 0000000..30a4703 Binary files /dev/null and b/browser/images/question.png differ diff --git a/browser/images/ui-tab--plus.png b/browser/images/ui-tab--plus.png new file mode 100755 index 0000000..de2fe4a Binary files /dev/null and b/browser/images/ui-tab--plus.png differ diff --git a/browser_tabbed/browser_tabbed.py b/browser_tabbed/browser_tabbed.py new file mode 100644 index 0000000..4b8c0fb --- /dev/null +++ b/browser_tabbed/browser_tabbed.py @@ -0,0 +1,255 @@ +from PyQt5.QtCore import * +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * +from PyQt5.QtWebEngineWidgets import * +from PyQt5.QtPrintSupport import * + +import os +import sys + + +class AboutDialog(QDialog): + def __init__(self, *args, **kwargs): + super(AboutDialog, self).__init__(*args, **kwargs) + + QBtn = QDialogButtonBox.Ok # No cancel + self.buttonBox = QDialogButtonBox(QBtn) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + layout = QVBoxLayout() + + title = QLabel("Mozarella Ashbadger") + font = title.font() + font.setPointSize(20) + title.setFont(font) + + layout.addWidget(title) + + logo = QLabel() + logo.setPixmap(QPixmap(os.path.join('images', 'ma-icon-128.png'))) + layout.addWidget(logo) + + layout.addWidget(QLabel("Version 23.35.211.233232")) + layout.addWidget(QLabel("Copyright 2015 Mozarella Inc.")) + + for i in range(0, layout.count()): + layout.itemAt(i).setAlignment(Qt.AlignHCenter) + + layout.addWidget(self.buttonBox) + + self.setLayout(layout) + + +class MainWindow(QMainWindow): + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + self.tabs = QTabWidget() + self.tabs.setDocumentMode(True) + self.tabs.tabBarDoubleClicked.connect(self.tab_open_doubleclick) + self.tabs.currentChanged.connect(self.current_tab_changed) + self.tabs.setTabsClosable(True) + self.tabs.tabCloseRequested.connect(self.close_current_tab) + + self.setCentralWidget(self.tabs) + + self.status = QStatusBar() + self.setStatusBar(self.status) + + navtb = QToolBar("Navigation") + navtb.setIconSize(QSize(16, 16)) + self.addToolBar(navtb) + + back_btn = QAction(QIcon(os.path.join('images', 'arrow-180.png')), "Back", self) + back_btn.setStatusTip("Back to previous page") + back_btn.triggered.connect(lambda: self.tabs.currentWidget().back()) + navtb.addAction(back_btn) + + next_btn = QAction(QIcon(os.path.join('images', 'arrow-000.png')), "Forward", self) + next_btn.setStatusTip("Forward to next page") + next_btn.triggered.connect(lambda: self.tabs.currentWidget().forward()) + navtb.addAction(next_btn) + + reload_btn = QAction(QIcon(os.path.join('images', 'arrow-circle-315.png')), "Reload", self) + reload_btn.setStatusTip("Reload page") + reload_btn.triggered.connect(lambda: self.tabs.currentWidget().reload()) + navtb.addAction(reload_btn) + + home_btn = QAction(QIcon(os.path.join('images', 'home.png')), "Home", self) + home_btn.setStatusTip("Go home") + home_btn.triggered.connect(self.navigate_home) + navtb.addAction(home_btn) + + navtb.addSeparator() + + self.httpsicon = QLabel() # Yes, really! + self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-nossl.png'))) + navtb.addWidget(self.httpsicon) + + self.urlbar = QLineEdit() + self.urlbar.returnPressed.connect(self.navigate_to_url) + navtb.addWidget(self.urlbar) + + stop_btn = QAction(QIcon(os.path.join('images', 'cross-circle.png')), "Stop", self) + stop_btn.setStatusTip("Stop loading current page") + stop_btn.triggered.connect(lambda: self.tabs.currentWidget().stop()) + navtb.addAction(stop_btn) + + # Uncomment to disable native menubar on Mac + # self.menuBar().setNativeMenuBar(False) + + file_menu = self.menuBar().addMenu("&File") + + new_tab_action = QAction(QIcon(os.path.join('images', 'ui-tab--plus.png')), "New Tab", self) + new_tab_action.setStatusTip("Open a new tab") + new_tab_action.triggered.connect(lambda _: self.add_new_tab()) + file_menu.addAction(new_tab_action) + + open_file_action = QAction(QIcon(os.path.join('images', 'disk--arrow.png')), "Open file...", self) + open_file_action.setStatusTip("Open from file") + open_file_action.triggered.connect(self.open_file) + file_menu.addAction(open_file_action) + + save_file_action = QAction(QIcon(os.path.join('images', 'disk--pencil.png')), "Save Page As...", self) + save_file_action.setStatusTip("Save current page to file") + save_file_action.triggered.connect(self.save_file) + file_menu.addAction(save_file_action) + + print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) + print_action.setStatusTip("Print current page") + print_action.triggered.connect(self.print_page) + file_menu.addAction(print_action) + + help_menu = self.menuBar().addMenu("&Help") + + about_action = QAction(QIcon(os.path.join('images', 'question.png')), "About Mozarella Ashbadger", self) + about_action.setStatusTip("Find out more about Mozarella Ashbadger") # Hungry! + about_action.triggered.connect(self.about) + help_menu.addAction(about_action) + + navigate_mozarella_action = QAction(QIcon(os.path.join('images', 'lifebuoy.png')), + "Mozarella Ashbadger Homepage", self) + navigate_mozarella_action.setStatusTip("Go to Mozarella Ashbadger Homepage") + navigate_mozarella_action.triggered.connect(self.navigate_mozarella) + help_menu.addAction(navigate_mozarella_action) + + self.add_new_tab(QUrl('http://www.google.com'), 'Homepage') + + self.show() + + self.setWindowTitle("Mozarella Ashbadger") + self.setWindowIcon(QIcon(os.path.join('images', 'ma-icon-64.png'))) + + def add_new_tab(self, qurl=None, label="Blank"): + + if qurl is None: + qurl = QUrl('') + + browser = QWebEngineView() + browser.setUrl(qurl) + i = self.tabs.addTab(browser, label) + + self.tabs.setCurrentIndex(i) + + # More difficult! We only want to update the url when it's from the + # correct tab + browser.urlChanged.connect(lambda qurl, browser=browser: + self.update_urlbar(qurl, browser)) + + browser.loadFinished.connect(lambda _, i=i, browser=browser: + self.tabs.setTabText(i, browser.page().title())) + + def tab_open_doubleclick(self, i): + if i == -1: # No tab under the click + self.add_new_tab() + + def current_tab_changed(self, i): + qurl = self.tabs.currentWidget().url() + self.update_urlbar(qurl, self.tabs.currentWidget()) + self.update_title(self.tabs.currentWidget()) + + def close_current_tab(self, i): + if self.tabs.count() < 2: + return + + self.tabs.removeTab(i) + + def update_title(self, browser): + if browser != self.tabs.currentWidget(): + # If this signal is not from the current tab, ignore + return + + title = self.tabs.currentWidget().page().title() + self.setWindowTitle("%s - Mozarella Ashbadger" % title) + + def navigate_mozarella(self): + self.tabs.currentWidget().setUrl(QUrl("https://www.udemy.com/522076")) + + def about(self): + dlg = AboutDialog() + dlg.exec_() + + def open_file(self): + filename, _ = QFileDialog.getOpenFileName(self, "Open file", "", + "Hypertext Markup Language (*.htm *.html);;" + "All files (*.*)") + + if filename: + with open(filename, 'r') as f: + html = f.read() + + self.tabs.currentWidget().setHtml(html) + self.urlbar.setText(filename) + + def save_file(self): + filename, _ = QFileDialog.getSaveFileName(self, "Save Page As", "", + "Hypertext Markup Language (*.htm *html);;" + "All files (*.*)") + + if filename: + html = self.tabs.currentWidget().page().mainFrame().toHtml() + with open(filename, 'w') as f: + f.write(html.encode('utf8')) + + def print_page(self): + dlg = QPrintPreviewDialog() + dlg.paintRequested.connect(self.browser.print_) + dlg.exec_() + + def navigate_home(self): + self.tabs.currentWidget().setUrl(QUrl("http://www.google.com")) + + def navigate_to_url(self): # Does not receive the Url + q = QUrl(self.urlbar.text()) + if q.scheme() == "": + q.setScheme("http") + + self.tabs.currentWidget().setUrl(q) + + def update_urlbar(self, q, browser=None): + + if browser != self.tabs.currentWidget(): + # If this signal is not from the current tab, ignore + return + + if q.scheme() == 'https': + # Secure padlock icon + self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-ssl.png'))) + + else: + # Insecure padlock icon + self.httpsicon.setPixmap(QPixmap(os.path.join('images', 'lock-nossl.png'))) + + self.urlbar.setText(q.toString()) + self.urlbar.setCursorPosition(0) + + +app = QApplication(sys.argv) +app.setApplicationName("Mozarella Ashbadger") +app.setOrganizationName("Mozarella") +app.setOrganizationDomain("mozarella.org") + +window = MainWindow() + +app.exec_() diff --git a/browser_tabbed/images/arrow-000.png b/browser_tabbed/images/arrow-000.png new file mode 100644 index 0000000..b39a43b Binary files /dev/null and b/browser_tabbed/images/arrow-000.png differ diff --git a/browser_tabbed/images/arrow-180.png b/browser_tabbed/images/arrow-180.png new file mode 100755 index 0000000..fc13555 Binary files /dev/null and b/browser_tabbed/images/arrow-180.png differ diff --git a/browser_tabbed/images/arrow-circle-315.png b/browser_tabbed/images/arrow-circle-315.png new file mode 100755 index 0000000..8baf084 Binary files /dev/null and b/browser_tabbed/images/arrow-circle-315.png differ diff --git a/browser_tabbed/images/cross-circle.png b/browser_tabbed/images/cross-circle.png new file mode 100755 index 0000000..20d6f5e Binary files /dev/null and b/browser_tabbed/images/cross-circle.png differ diff --git a/browser_tabbed/images/cross.png b/browser_tabbed/images/cross.png new file mode 100755 index 0000000..6b3d517 Binary files /dev/null and b/browser_tabbed/images/cross.png differ diff --git a/browser_tabbed/images/disk--arrow.png b/browser_tabbed/images/disk--arrow.png new file mode 100755 index 0000000..bd58103 Binary files /dev/null and b/browser_tabbed/images/disk--arrow.png differ diff --git a/browser_tabbed/images/disk--pencil.png b/browser_tabbed/images/disk--pencil.png new file mode 100755 index 0000000..47bf953 Binary files /dev/null and b/browser_tabbed/images/disk--pencil.png differ diff --git a/browser_tabbed/images/home.png b/browser_tabbed/images/home.png new file mode 100755 index 0000000..622a2b7 Binary files /dev/null and b/browser_tabbed/images/home.png differ diff --git a/browser_tabbed/images/lifebuoy.png b/browser_tabbed/images/lifebuoy.png new file mode 100755 index 0000000..2fb98bf Binary files /dev/null and b/browser_tabbed/images/lifebuoy.png differ diff --git a/browser_tabbed/images/lock-nossl.png b/browser_tabbed/images/lock-nossl.png new file mode 100755 index 0000000..006528c Binary files /dev/null and b/browser_tabbed/images/lock-nossl.png differ diff --git a/browser_tabbed/images/lock-ssl.png b/browser_tabbed/images/lock-ssl.png new file mode 100755 index 0000000..34b7748 Binary files /dev/null and b/browser_tabbed/images/lock-ssl.png differ diff --git a/browser_tabbed/images/ma-icon-128.png b/browser_tabbed/images/ma-icon-128.png new file mode 100644 index 0000000..bd04bd3 Binary files /dev/null and b/browser_tabbed/images/ma-icon-128.png differ diff --git a/browser_tabbed/images/ma-icon-256.png b/browser_tabbed/images/ma-icon-256.png new file mode 100644 index 0000000..d986c67 Binary files /dev/null and b/browser_tabbed/images/ma-icon-256.png differ diff --git a/browser_tabbed/images/ma-icon-64.png b/browser_tabbed/images/ma-icon-64.png new file mode 100644 index 0000000..884fcb7 Binary files /dev/null and b/browser_tabbed/images/ma-icon-64.png differ diff --git a/browser_tabbed/images/printer.png b/browser_tabbed/images/printer.png new file mode 100755 index 0000000..9edfbbc Binary files /dev/null and b/browser_tabbed/images/printer.png differ diff --git a/browser_tabbed/images/question.png b/browser_tabbed/images/question.png new file mode 100755 index 0000000..30a4703 Binary files /dev/null and b/browser_tabbed/images/question.png differ diff --git a/browser_tabbed/images/ui-tab--plus.png b/browser_tabbed/images/ui-tab--plus.png new file mode 100755 index 0000000..de2fe4a Binary files /dev/null and b/browser_tabbed/images/ui-tab--plus.png differ diff --git a/calculator/MainWindow.py b/calculator/MainWindow.py new file mode 100644 index 0000000..a8655c1 --- /dev/null +++ b/calculator/MainWindow.py @@ -0,0 +1,318 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'mainwindow.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(484, 433) + self.centralWidget = QtWidgets.QWidget(MainWindow) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralWidget.sizePolicy().hasHeightForWidth()) + self.centralWidget.setSizePolicy(sizePolicy) + self.centralWidget.setObjectName("centralWidget") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralWidget) + self.verticalLayout_2.setContentsMargins(11, 11, 11, 11) + self.verticalLayout_2.setSpacing(6) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setSpacing(6) + self.verticalLayout.setObjectName("verticalLayout") + self.lcdNumber = QtWidgets.QLCDNumber(self.centralWidget) + self.lcdNumber.setDigitCount(10) + self.lcdNumber.setObjectName("lcdNumber") + self.verticalLayout.addWidget(self.lcdNumber) + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setSpacing(6) + self.gridLayout.setObjectName("gridLayout") + self.pushButton_n4 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n4.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n4.setFont(font) + self.pushButton_n4.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n4.setObjectName("pushButton_n4") + self.gridLayout.addWidget(self.pushButton_n4, 3, 0, 1, 1) + self.pushButton_n1 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n1.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n1.setFont(font) + self.pushButton_n1.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n1.setObjectName("pushButton_n1") + self.gridLayout.addWidget(self.pushButton_n1, 4, 0, 1, 1) + self.pushButton_n8 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n8.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n8.setFont(font) + self.pushButton_n8.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n8.setObjectName("pushButton_n8") + self.gridLayout.addWidget(self.pushButton_n8, 2, 1, 1, 1) + self.pushButton_mul = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_mul.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_mul.setFont(font) + self.pushButton_mul.setObjectName("pushButton_mul") + self.gridLayout.addWidget(self.pushButton_mul, 2, 3, 1, 1) + self.pushButton_n7 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n7.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n7.setFont(font) + self.pushButton_n7.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n7.setObjectName("pushButton_n7") + self.gridLayout.addWidget(self.pushButton_n7, 2, 0, 1, 1) + self.pushButton_n6 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n6.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n6.setFont(font) + self.pushButton_n6.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n6.setObjectName("pushButton_n6") + self.gridLayout.addWidget(self.pushButton_n6, 3, 2, 1, 1) + self.pushButton_n5 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n5.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n5.setFont(font) + self.pushButton_n5.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n5.setObjectName("pushButton_n5") + self.gridLayout.addWidget(self.pushButton_n5, 3, 1, 1, 1) + self.pushButton_n0 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n0.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n0.setFont(font) + self.pushButton_n0.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n0.setObjectName("pushButton_n0") + self.gridLayout.addWidget(self.pushButton_n0, 5, 0, 1, 1) + self.pushButton_n2 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n2.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n2.setFont(font) + self.pushButton_n2.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n2.setObjectName("pushButton_n2") + self.gridLayout.addWidget(self.pushButton_n2, 4, 1, 1, 1) + self.pushButton_n9 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n9.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n9.setFont(font) + self.pushButton_n9.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n9.setObjectName("pushButton_n9") + self.gridLayout.addWidget(self.pushButton_n9, 2, 2, 1, 1) + self.pushButton_n3 = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_n3.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_n3.setFont(font) + self.pushButton_n3.setStyleSheet("QPushButton {\n" +"color: #1976D2;\n" +"}") + self.pushButton_n3.setObjectName("pushButton_n3") + self.gridLayout.addWidget(self.pushButton_n3, 4, 2, 1, 1) + self.pushButton_div = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_div.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_div.setFont(font) + self.pushButton_div.setObjectName("pushButton_div") + self.gridLayout.addWidget(self.pushButton_div, 1, 3, 1, 1) + self.pushButton_sub = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_sub.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_sub.setFont(font) + self.pushButton_sub.setObjectName("pushButton_sub") + self.gridLayout.addWidget(self.pushButton_sub, 3, 3, 1, 1) + self.pushButton_add = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_add.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_add.setFont(font) + self.pushButton_add.setObjectName("pushButton_add") + self.gridLayout.addWidget(self.pushButton_add, 4, 3, 1, 1) + self.pushButton_ac = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_ac.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_ac.setFont(font) + self.pushButton_ac.setStyleSheet("QPushButton {\n" +" color: #f44336;\n" +"}") + self.pushButton_ac.setObjectName("pushButton_ac") + self.gridLayout.addWidget(self.pushButton_ac, 1, 0, 1, 1) + self.pushButton_mr = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_mr.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_mr.setFont(font) + self.pushButton_mr.setStyleSheet("QPushButton {\n" +" color: #FFC107;\n" +"}") + self.pushButton_mr.setObjectName("pushButton_mr") + self.gridLayout.addWidget(self.pushButton_mr, 1, 2, 1, 1) + self.pushButton_m = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_m.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_m.setFont(font) + self.pushButton_m.setStyleSheet("QPushButton {\n" +" color: #FFC107;\n" +"}") + self.pushButton_m.setObjectName("pushButton_m") + self.gridLayout.addWidget(self.pushButton_m, 1, 1, 1, 1) + self.pushButton_pc = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_pc.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(False) + font.setWeight(50) + self.pushButton_pc.setFont(font) + self.pushButton_pc.setObjectName("pushButton_pc") + self.gridLayout.addWidget(self.pushButton_pc, 5, 1, 1, 1) + self.pushButton_eq = QtWidgets.QPushButton(self.centralWidget) + self.pushButton_eq.setMinimumSize(QtCore.QSize(0, 50)) + font = QtGui.QFont() + font.setPointSize(27) + font.setBold(True) + font.setWeight(75) + self.pushButton_eq.setFont(font) + self.pushButton_eq.setStyleSheet("QPushButton {\n" +"color: #4CAF50;\n" +"}") + self.pushButton_eq.setObjectName("pushButton_eq") + self.gridLayout.addWidget(self.pushButton_eq, 5, 2, 1, 2) + self.verticalLayout.addLayout(self.gridLayout) + self.verticalLayout_2.addLayout(self.verticalLayout) + MainWindow.setCentralWidget(self.centralWidget) + self.menuBar = QtWidgets.QMenuBar(MainWindow) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 484, 22)) + self.menuBar.setObjectName("menuBar") + self.menuFIle = QtWidgets.QMenu(self.menuBar) + self.menuFIle.setObjectName("menuFIle") + MainWindow.setMenuBar(self.menuBar) + self.statusBar = QtWidgets.QStatusBar(MainWindow) + self.statusBar.setObjectName("statusBar") + MainWindow.setStatusBar(self.statusBar) + self.actionExit = QtWidgets.QAction(MainWindow) + self.actionExit.setObjectName("actionExit") + self.actionReset = QtWidgets.QAction(MainWindow) + self.actionReset.setObjectName("actionReset") + self.menuFIle.addAction(self.actionReset) + self.menuFIle.addAction(self.actionExit) + self.menuBar.addAction(self.menuFIle.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Calculon")) + self.pushButton_n4.setText(_translate("MainWindow", "4")) + self.pushButton_n4.setShortcut(_translate("MainWindow", "4")) + self.pushButton_n1.setText(_translate("MainWindow", "1")) + self.pushButton_n1.setShortcut(_translate("MainWindow", "1")) + self.pushButton_n8.setText(_translate("MainWindow", "8")) + self.pushButton_n8.setShortcut(_translate("MainWindow", "8")) + self.pushButton_mul.setText(_translate("MainWindow", "x")) + self.pushButton_mul.setShortcut(_translate("MainWindow", "*")) + self.pushButton_n7.setText(_translate("MainWindow", "7")) + self.pushButton_n7.setShortcut(_translate("MainWindow", "7")) + self.pushButton_n6.setText(_translate("MainWindow", "6")) + self.pushButton_n6.setShortcut(_translate("MainWindow", "6")) + self.pushButton_n5.setText(_translate("MainWindow", "5")) + self.pushButton_n5.setShortcut(_translate("MainWindow", "5")) + self.pushButton_n0.setText(_translate("MainWindow", "0")) + self.pushButton_n0.setShortcut(_translate("MainWindow", "0")) + self.pushButton_n2.setText(_translate("MainWindow", "2")) + self.pushButton_n2.setShortcut(_translate("MainWindow", "2")) + self.pushButton_n9.setText(_translate("MainWindow", "9")) + self.pushButton_n9.setShortcut(_translate("MainWindow", "9")) + self.pushButton_n3.setText(_translate("MainWindow", "3")) + self.pushButton_n3.setShortcut(_translate("MainWindow", "3")) + self.pushButton_div.setText(_translate("MainWindow", "÷")) + self.pushButton_div.setShortcut(_translate("MainWindow", "/")) + self.pushButton_sub.setText(_translate("MainWindow", "-")) + self.pushButton_sub.setShortcut(_translate("MainWindow", "-")) + self.pushButton_add.setText(_translate("MainWindow", "+")) + self.pushButton_add.setShortcut(_translate("MainWindow", "+")) + self.pushButton_ac.setText(_translate("MainWindow", "AC")) + self.pushButton_ac.setShortcut(_translate("MainWindow", "Esc")) + self.pushButton_mr.setText(_translate("MainWindow", "MR")) + self.pushButton_mr.setShortcut(_translate("MainWindow", "R")) + self.pushButton_m.setText(_translate("MainWindow", "M")) + self.pushButton_m.setShortcut(_translate("MainWindow", "M")) + self.pushButton_pc.setText(_translate("MainWindow", "%")) + self.pushButton_pc.setShortcut(_translate("MainWindow", "%")) + self.pushButton_eq.setText(_translate("MainWindow", "=")) + self.pushButton_eq.setShortcut(_translate("MainWindow", "Return")) + self.menuFIle.setTitle(_translate("MainWindow", "FIle")) + self.actionExit.setText(_translate("MainWindow", "Exit")) + self.actionExit.setShortcut(_translate("MainWindow", "Ctrl+Q")) + self.actionReset.setText(_translate("MainWindow", "Reset")) + self.actionReset.setShortcut(_translate("MainWindow", "Ctrl+R")) + diff --git a/calculator/calculator.py b/calculator/calculator.py new file mode 100644 index 0000000..ed4e897 --- /dev/null +++ b/calculator/calculator.py @@ -0,0 +1,105 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * + +import operator + +from MainWindow import Ui_MainWindow + +# Calculator state. +READY = 0 +INPUT = 1 + + +class MainWindow(QMainWindow, Ui_MainWindow): + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + self.setupUi(self) + + # Setup numbers. + for n in range(0, 10): + getattr(self, 'pushButton_n%s' % n).pressed.connect(lambda v=n: self.input_number(v)) + + # Setup operations. + self.pushButton_add.pressed.connect(lambda: self.operation(operator.add)) + self.pushButton_sub.pressed.connect(lambda: self.operation(operator.sub)) + self.pushButton_mul.pressed.connect(lambda: self.operation(operator.mul)) + self.pushButton_div.pressed.connect(lambda: self.operation(operator.truediv)) # operator.div for Python2.7 + + self.pushButton_pc.pressed.connect(self.operation_pc) + self.pushButton_eq.pressed.connect(self.equals) + + # Setup actions + self.actionReset.triggered.connect(self.reset) + self.pushButton_ac.pressed.connect(self.reset) + + self.pushButton_m.pressed.connect(self.memory_store) + self.pushButton_mr.pressed.connect(self.memory_recall) + + self.memory = 0 + self.reset() + + self.show() + + def display(self): + self.lcdNumber.display(self.stack[-1]) + + def reset(self): + self.state = READY + self.stack = [0] + self.last_operation = None + self.current_op = None + self.display() + + def memory_store(self): + self.memory = self.lcdNumber.value() + + def memory_recall(self): + self.state = INPUT + self.stack[-1] = self.memory + self.display() + + def input_number(self, v): + if self.state == READY: + self.state = INPUT + self.stack[-1] = v + else: + self.stack[-1] = self.stack[-1] * 10 + v + + self.display() + + def operation(self, op): + if self.current_op: # Complete the current operation + self.equals() + + self.stack.append(0) + self.state = INPUT + self.current_op = op + + def operation_pc(self): + self.state = INPUT + self.stack[-1] *= 0.01 + self.display() + + def equals(self): + # Support to allow '=' to repeat previous operation + # if no further input has been added. + if self.state == READY and self.last_operation: + s, self.current_op = self.last_operation + self.stack.append(s) + + if self.current_op: + self.last_operation = self.stack[-1], self.current_op + + self.stack = [self.current_op(*self.stack)] + self.current_op = None + self.state = READY + self.display() + + +if __name__ == '__main__': + app = QApplication([]) + app.setApplicationName("Calculon") + + window = MainWindow() + app.exec_() diff --git a/calculator/mainwindow.ui b/calculator/mainwindow.ui new file mode 100644 index 0000000..5d14863 --- /dev/null +++ b/calculator/mainwindow.ui @@ -0,0 +1,583 @@ + + + MainWindow + + + + 0 + 0 + 484 + 433 + + + + Calculon + + + + + 0 + 0 + + + + + + + 10 + + + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 4 + + + 4 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 1 + + + 1 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 8 + + + 8 + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + x + + + * + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 7 + + + 7 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 6 + + + 6 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 5 + + + 5 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 0 + + + 0 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 2 + + + 2 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 9 + + + 9 + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #1976D2; +} + + + 3 + + + 3 + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + ÷ + + + / + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + - + + + - + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + + + + + + + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + QPushButton { + color: #f44336; +} + + + AC + + + Esc + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + QPushButton { + color: #FFC107; +} + + + MR + + + R + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + QPushButton { + color: #FFC107; +} + + + M + + + M + + + + + + + + 0 + 50 + + + + + 27 + 50 + false + + + + % + + + % + + + + + + + + 0 + 50 + + + + + 27 + 75 + true + + + + QPushButton { +color: #4CAF50; +} + + + = + + + Return + + + + + + + + + + + 0 + 0 + 484 + 22 + + + + + FIle + + + + + + + + + + Exit + + + Ctrl+Q + + + + + Reset + + + Ctrl+R + + + + + + + diff --git a/camera/camera.py b/camera/camera.py new file mode 100644 index 0000000..e3b948c --- /dev/null +++ b/camera/camera.py @@ -0,0 +1,107 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtPrintSupport import * +from PyQt5.QtMultimedia import * +from PyQt5.QtMultimediaWidgets import * + +import os +import sys +import time + + +class MainWindow(QMainWindow): + + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + self.available_cameras = QCameraInfo.availableCameras() + if not self.available_cameras: + pass #quit + + self.status = QStatusBar() + self.setStatusBar(self.status) + + + self.save_path = "" + + self.viewfinder = QCameraViewfinder() + self.viewfinder.show() + self.setCentralWidget(self.viewfinder) + + # Set the default camera. + self.select_camera(0) + + # Setup tools + camera_toolbar = QToolBar("Camera") + camera_toolbar.setIconSize(QSize(14, 14)) + self.addToolBar(camera_toolbar) + + photo_action = QAction(QIcon(os.path.join('images', 'camera-black.png')), "Take photo...", self) + photo_action.setStatusTip("Take photo of current view") + photo_action.triggered.connect(self.take_photo) + camera_toolbar.addAction(photo_action) + + change_folder_action = QAction(QIcon(os.path.join('images', 'blue-folder-horizontal-open.png')), "Change save location...", self) + change_folder_action.setStatusTip("Change folder where photos are saved.") + change_folder_action.triggered.connect(self.change_folder) + camera_toolbar.addAction(change_folder_action) + + + camera_selector = QComboBox() + camera_selector.addItems([c.description() for c in self.available_cameras]) + camera_selector.currentIndexChanged.connect( self.select_camera ) + + camera_toolbar.addWidget(camera_selector) + + + self.setWindowTitle("NSAViewer") + self.show() + + def select_camera(self, i): + self.camera = QCamera(self.available_cameras[i]) + self.camera.setViewfinder(self.viewfinder) + self.camera.setCaptureMode(QCamera.CaptureStillImage) + self.camera.error.connect(lambda: self.alert(self.camera.errorString())) + self.camera.start() + + self.capture = QCameraImageCapture(self.camera) + self.capture.error.connect(lambda i, e, s: self.alert(s)) + self.capture.imageCaptured.connect(lambda d, i: self.status.showMessage("Image %04d captured" % self.save_seq)) + + self.current_camera_name = self.available_cameras[i].description() + self.save_seq = 0 + + def take_photo(self): + self.viewfinder.setContrast(100) + #self.viewfinder.setBrightness(0) + + timestamp = time.strftime("%d-%b-%Y-%H_%M_%S") + self.capture.capture(os.path.join(self.save_path, "%s-%04d-%s.jpg" % ( + self.current_camera_name, + self.save_seq, + timestamp + ))) + self.save_seq += 1 + + def change_folder(self): + path = QFileDialog.getExistingDirectory(self, "Snapshot save location", "") + if path: + self.save_path = path + self.save_seq = 0 + + def alert(self, s): + """ + Handle errors coming from QCamera dn QCameraImageCapture by displaying alerts. + """ + err = QErrorMessage(self) + err.showMessage(s) + + +if __name__ == '__main__': + + app = QApplication(sys.argv) + app.setApplicationName("NSAViewer") + + window = MainWindow() + app.exec_() \ No newline at end of file diff --git a/camera/images/blue-folder-horizontal-open.png b/camera/images/blue-folder-horizontal-open.png new file mode 100755 index 0000000..f2454ae Binary files /dev/null and b/camera/images/blue-folder-horizontal-open.png differ diff --git a/camera/images/camera-black.png b/camera/images/camera-black.png new file mode 100755 index 0000000..9880aed Binary files /dev/null and b/camera/images/camera-black.png differ diff --git a/mediaplayer/MainWindow.py b/mediaplayer/MainWindow.py new file mode 100644 index 0000000..09df321 --- /dev/null +++ b/mediaplayer/MainWindow.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'mainwindow.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(484, 371) + self.centralWidget = QtWidgets.QWidget(MainWindow) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralWidget.sizePolicy().hasHeightForWidth()) + self.centralWidget.setSizePolicy(sizePolicy) + self.centralWidget.setObjectName("centralWidget") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralWidget) + self.horizontalLayout.setContentsMargins(11, 11, 11, 11) + self.horizontalLayout.setSpacing(6) + self.horizontalLayout.setObjectName("horizontalLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setSpacing(6) + self.verticalLayout.setObjectName("verticalLayout") + self.playlistView = QtWidgets.QListView(self.centralWidget) + self.playlistView.setAcceptDrops(True) + self.playlistView.setProperty("showDropIndicator", True) + self.playlistView.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly) + self.playlistView.setAlternatingRowColors(True) + self.playlistView.setUniformItemSizes(True) + self.playlistView.setObjectName("playlistView") + self.verticalLayout.addWidget(self.playlistView) + self.horizontalLayout_4 = QtWidgets.QHBoxLayout() + self.horizontalLayout_4.setSpacing(6) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.currentTimeLabel = QtWidgets.QLabel(self.centralWidget) + self.currentTimeLabel.setMinimumSize(QtCore.QSize(80, 0)) + self.currentTimeLabel.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.currentTimeLabel.setObjectName("currentTimeLabel") + self.horizontalLayout_4.addWidget(self.currentTimeLabel) + self.timeSlider = QtWidgets.QSlider(self.centralWidget) + self.timeSlider.setOrientation(QtCore.Qt.Horizontal) + self.timeSlider.setObjectName("timeSlider") + self.horizontalLayout_4.addWidget(self.timeSlider) + self.totalTimeLabel = QtWidgets.QLabel(self.centralWidget) + self.totalTimeLabel.setMinimumSize(QtCore.QSize(80, 0)) + self.totalTimeLabel.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.totalTimeLabel.setObjectName("totalTimeLabel") + self.horizontalLayout_4.addWidget(self.totalTimeLabel) + self.verticalLayout.addLayout(self.horizontalLayout_4) + self.horizontalLayout_5 = QtWidgets.QHBoxLayout() + self.horizontalLayout_5.setSpacing(6) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.previousButton = QtWidgets.QPushButton(self.centralWidget) + self.previousButton.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("images/control-skip-180.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.previousButton.setIcon(icon) + self.previousButton.setObjectName("previousButton") + self.horizontalLayout_5.addWidget(self.previousButton) + self.playButton = QtWidgets.QPushButton(self.centralWidget) + self.playButton.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("images/control.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.playButton.setIcon(icon1) + self.playButton.setObjectName("playButton") + self.horizontalLayout_5.addWidget(self.playButton) + self.pauseButton = QtWidgets.QPushButton(self.centralWidget) + self.pauseButton.setText("") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("images/control-pause.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pauseButton.setIcon(icon2) + self.pauseButton.setObjectName("pauseButton") + self.horizontalLayout_5.addWidget(self.pauseButton) + self.stopButton = QtWidgets.QPushButton(self.centralWidget) + self.stopButton.setText("") + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap("images/control-stop-square.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.stopButton.setIcon(icon3) + self.stopButton.setObjectName("stopButton") + self.horizontalLayout_5.addWidget(self.stopButton) + self.nextButton = QtWidgets.QPushButton(self.centralWidget) + self.nextButton.setText("") + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap("images/control-skip.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.nextButton.setIcon(icon4) + self.nextButton.setObjectName("nextButton") + self.horizontalLayout_5.addWidget(self.nextButton) + self.viewButton = QtWidgets.QPushButton(self.centralWidget) + self.viewButton.setText("") + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap("images/application-image.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.viewButton.setIcon(icon5) + self.viewButton.setCheckable(True) + self.viewButton.setObjectName("viewButton") + self.horizontalLayout_5.addWidget(self.viewButton) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_5.addItem(spacerItem) + self.label = QtWidgets.QLabel(self.centralWidget) + self.label.setText("") + self.label.setPixmap(QtGui.QPixmap("images/speaker-volume.png")) + self.label.setObjectName("label") + self.horizontalLayout_5.addWidget(self.label) + self.volumeSlider = QtWidgets.QSlider(self.centralWidget) + self.volumeSlider.setMaximum(100) + self.volumeSlider.setProperty("value", 100) + self.volumeSlider.setOrientation(QtCore.Qt.Horizontal) + self.volumeSlider.setObjectName("volumeSlider") + self.horizontalLayout_5.addWidget(self.volumeSlider) + self.verticalLayout.addLayout(self.horizontalLayout_5) + self.horizontalLayout.addLayout(self.verticalLayout) + MainWindow.setCentralWidget(self.centralWidget) + self.menuBar = QtWidgets.QMenuBar(MainWindow) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 484, 22)) + self.menuBar.setObjectName("menuBar") + self.menuFIle = QtWidgets.QMenu(self.menuBar) + self.menuFIle.setObjectName("menuFIle") + MainWindow.setMenuBar(self.menuBar) + self.statusBar = QtWidgets.QStatusBar(MainWindow) + self.statusBar.setObjectName("statusBar") + MainWindow.setStatusBar(self.statusBar) + self.open_file_action = QtWidgets.QAction(MainWindow) + self.open_file_action.setObjectName("open_file_action") + self.menuFIle.addAction(self.open_file_action) + self.menuBar.addAction(self.menuFIle.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Failamp")) + self.currentTimeLabel.setText(_translate("MainWindow", "0:00")) + self.totalTimeLabel.setText(_translate("MainWindow", "0:00")) + self.menuFIle.setTitle(_translate("MainWindow", "FIle")) + self.open_file_action.setText(_translate("MainWindow", "Open file...")) + diff --git a/mediaplayer/images/application-image.png b/mediaplayer/images/application-image.png new file mode 100755 index 0000000..915d63f Binary files /dev/null and b/mediaplayer/images/application-image.png differ diff --git a/mediaplayer/images/control-pause.png b/mediaplayer/images/control-pause.png new file mode 100755 index 0000000..af57b25 Binary files /dev/null and b/mediaplayer/images/control-pause.png differ diff --git a/mediaplayer/images/control-skip-180.png b/mediaplayer/images/control-skip-180.png new file mode 100755 index 0000000..62daa7a Binary files /dev/null and b/mediaplayer/images/control-skip-180.png differ diff --git a/mediaplayer/images/control-skip.png b/mediaplayer/images/control-skip.png new file mode 100755 index 0000000..e4a4930 Binary files /dev/null and b/mediaplayer/images/control-skip.png differ diff --git a/mediaplayer/images/control-stop-square.png b/mediaplayer/images/control-stop-square.png new file mode 100755 index 0000000..7c6af7f Binary files /dev/null and b/mediaplayer/images/control-stop-square.png differ diff --git a/mediaplayer/images/control.png b/mediaplayer/images/control.png new file mode 100755 index 0000000..2dfaef5 Binary files /dev/null and b/mediaplayer/images/control.png differ diff --git a/mediaplayer/images/speaker-volume.png b/mediaplayer/images/speaker-volume.png new file mode 100755 index 0000000..62fcfc6 Binary files /dev/null and b/mediaplayer/images/speaker-volume.png differ diff --git a/mediaplayer/mainwindow.ui b/mediaplayer/mainwindow.ui new file mode 100644 index 0000000..2d8a4ba --- /dev/null +++ b/mediaplayer/mainwindow.ui @@ -0,0 +1,227 @@ + + + MainWindow + + + + 0 + 0 + 484 + 371 + + + + Failamp + + + + + 0 + 0 + + + + + + + true + + + true + + + QAbstractItemView::DropOnly + + + Qt::CopyAction + + + true + + + true + + + + + + + + + + 80 + 0 + + + + 0:00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + 80 + 0 + + + + 0:00 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + + + + + images/control-skip-180.pngimages/control-skip-180.png + + + + + + + + + + + images/control.pngimages/control.png + + + + + + + + + + + images/control-pause.pngimages/control-pause.png + + + + + + + + + + + images/control-stop-square.pngimages/control-stop-square.png + + + + + + + + + + + images/control-skip.pngimages/control-skip.png + + + + + + + + + + + images/application-image.pngimages/application-image.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + images/speaker-volume.png + + + + + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + + + + + 0 + 0 + 484 + 22 + + + + + FIle + + + + + + + + + Open file... + + + + + + + diff --git a/mediaplayer/mainwindow.ui.autosave b/mediaplayer/mainwindow.ui.autosave new file mode 100644 index 0000000..2d8a4ba --- /dev/null +++ b/mediaplayer/mainwindow.ui.autosave @@ -0,0 +1,227 @@ + + + MainWindow + + + + 0 + 0 + 484 + 371 + + + + Failamp + + + + + 0 + 0 + + + + + + + true + + + true + + + QAbstractItemView::DropOnly + + + Qt::CopyAction + + + true + + + true + + + + + + + + + + 80 + 0 + + + + 0:00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + 80 + 0 + + + + 0:00 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + + + + + images/control-skip-180.pngimages/control-skip-180.png + + + + + + + + + + + images/control.pngimages/control.png + + + + + + + + + + + images/control-pause.pngimages/control-pause.png + + + + + + + + + + + images/control-stop-square.pngimages/control-stop-square.png + + + + + + + + + + + images/control-skip.pngimages/control-skip.png + + + + + + + + + + + images/application-image.pngimages/application-image.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + images/speaker-volume.png + + + + + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + + + + + 0 + 0 + 484 + 22 + + + + + FIle + + + + + + + + + Open file... + + + + + + + diff --git a/mediaplayer/mediaplayer.py b/mediaplayer/mediaplayer.py new file mode 100644 index 0000000..2e51ca7 --- /dev/null +++ b/mediaplayer/mediaplayer.py @@ -0,0 +1,184 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtMultimedia import * +from PyQt5.QtMultimediaWidgets import * + +from MainWindow import Ui_MainWindow + +def hhmmss(ms): + # s = 1000 + # m = 60000 + # h = 360000 + h, r = divmod(ms, 36000) + m, r = divmod(r, 60000) + s, _ = divmod(r, 1000) + return ("%d:%02d:%02d" % (h,m,s)) if h else ("%d:%02d" % (m,s)) + +class ViewerWindow(QMainWindow): + state = pyqtSignal(bool) + + def closeEvent(self, e): + # Emit the window state, to update the viewer toggle button. + self.state.emit(False) + + +class PlaylistModel(QAbstractListModel): + def __init__(self, playlist, *args, **kwargs): + super(PlaylistModel, self).__init__(*args, **kwargs) + self.playlist = playlist + + def data(self, index, role): + if role == Qt.DisplayRole: + media = self.playlist.media(index.row()) + return media.canonicalUrl().fileName() + + def rowCount(self, index): + return self.playlist.mediaCount() + + +class MainWindow(QMainWindow, Ui_MainWindow): + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + self.setupUi(self) + + self.player = QMediaPlayer() + + self.player.error.connect(self.erroralert) + self.player.play() + + # Setup the playlist. + self.playlist = QMediaPlaylist() + self.player.setPlaylist(self.playlist) + + # Add viewer for video playback, separate floating window. + self.viewer = ViewerWindow(self) + self.viewer.setWindowFlags(self.viewer.windowFlags() | Qt.WindowStaysOnTopHint) + self.viewer.setMinimumSize(QSize(480,360)) + + videoWidget = QVideoWidget() + self.viewer.setCentralWidget(videoWidget) + self.player.setVideoOutput(videoWidget) + + # Connect control buttons/slides for media player. + self.playButton.pressed.connect(self.player.play) + self.pauseButton.pressed.connect(self.player.pause) + self.stopButton.pressed.connect(self.player.stop) + self.volumeSlider.valueChanged.connect(self.player.setVolume) + + self.viewButton.toggled.connect(self.toggle_viewer) + self.viewer.state.connect(self.viewButton.setChecked) + + self.previousButton.pressed.connect(self.playlist.previous) + self.nextButton.pressed.connect(self.playlist.next) + + self.model = PlaylistModel(self.playlist) + self.playlistView.setModel(self.model) + self.playlist.currentIndexChanged.connect(self.playlist_position_changed) + selection_model = self.playlistView.selectionModel() + selection_model.selectionChanged.connect(self.playlist_selection_changed) + + self.player.durationChanged.connect(self.update_duration) + self.player.positionChanged.connect(self.update_position) + self.timeSlider.valueChanged.connect(self.player.setPosition) + + self.open_file_action.triggered.connect(self.open_file) + + self.setAcceptDrops(True) + + self.show() + + def dragEnterEvent(self, e): + if e.mimeData().hasUrls(): + e.acceptProposedAction() + + def dropEvent(self, e): + for url in e.mimeData().urls(): + self.playlist.addMedia( + QMediaContent(url) + ) + + self.model.layoutChanged.emit() + + # If not playing, seeking to first of newly added + play. + if self.player.state() != QMediaPlayer.PlayingState: + i = self.playlist.mediaCount() - len(e.mimeData().urls()) + self.playlist.setCurrentIndex(i) + self.player.play() + + def open_file(self): + path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "mp3 Audio (*.mp3);mp4 Video (*.mp4);Movie files (*.mov);All files (*.*)") + + if path: + self.playlist.addMedia( + QMediaContent( + QUrl.fromLocalFile(path) + ) + ) + + self.model.layoutChanged.emit() + + def update_duration(self, mc): + self.timeSlider.setMaximum(self.player.duration()) + duration = self.player.duration() + + if duration >= 0: + self.totalTimeLabel.setText(hhmmss(duration)) + + def update_position(self, *args): + position = self.player.position() + if position >= 0: + self.currentTimeLabel.setText(hhmmss(position)) + + # Disable the events to prevent updating triggering a setPosition event (can cause stuttering). + self.timeSlider.blockSignals(True) + self.timeSlider.setValue(position) + self.timeSlider.blockSignals(False) + + def playlist_selection_changed(self, ix): + # We receive a QItemSelection from selectionChanged. + i = ix.indexes()[0].row() + self.playlist.setCurrentIndex(i) + + def playlist_position_changed(self, i): + if i > -1: + ix = self.model.index(i) + self.playlistView.setCurrentIndex(ix) + + def toggle_viewer(self, state): + if state: + self.viewer.show() + else: + self.viewer.hide() + + def erroralert(self, *args): + print(args) + + + + +if __name__ == '__main__': + app = QApplication([]) + app.setApplicationName("Failamp") + app.setStyle("Fusion") + + # Fusion dark palette from https://gist.github.com/QuantumCD/6245215. + palette = QPalette() + palette.setColor(QPalette.Window, QColor(53, 53, 53)) + palette.setColor(QPalette.WindowText, Qt.white) + palette.setColor(QPalette.Base, QColor(25, 25, 25)) + palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) + palette.setColor(QPalette.ToolTipBase, Qt.white) + palette.setColor(QPalette.ToolTipText, Qt.white) + palette.setColor(QPalette.Text, Qt.white) + palette.setColor(QPalette.Button, QColor(53, 53, 53)) + palette.setColor(QPalette.ButtonText, Qt.white) + palette.setColor(QPalette.BrightText, Qt.red) + palette.setColor(QPalette.Link, QColor(42, 130, 218)) + palette.setColor(QPalette.Highlight, QColor(42, 130, 218)) + palette.setColor(QPalette.HighlightedText, Qt.black) + app.setPalette(palette) + app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }") + + window = MainWindow() + app.exec_() diff --git a/minesweeper/images/bomb.png b/minesweeper/images/bomb.png new file mode 100755 index 0000000..2e73168 Binary files /dev/null and b/minesweeper/images/bomb.png differ diff --git a/minesweeper/images/bug.png b/minesweeper/images/bug.png new file mode 100755 index 0000000..242d539 Binary files /dev/null and b/minesweeper/images/bug.png differ diff --git a/minesweeper/images/cactus.png b/minesweeper/images/cactus.png new file mode 100755 index 0000000..bcd705e Binary files /dev/null and b/minesweeper/images/cactus.png differ diff --git a/minesweeper/images/cake.png b/minesweeper/images/cake.png new file mode 100755 index 0000000..c3bb699 Binary files /dev/null and b/minesweeper/images/cake.png differ diff --git a/minesweeper/images/clock-select.png b/minesweeper/images/clock-select.png new file mode 100755 index 0000000..8c56791 Binary files /dev/null and b/minesweeper/images/clock-select.png differ diff --git a/minesweeper/images/cross.png b/minesweeper/images/cross.png new file mode 100755 index 0000000..6b3d517 Binary files /dev/null and b/minesweeper/images/cross.png differ diff --git a/minesweeper/images/cup.png b/minesweeper/images/cup.png new file mode 100755 index 0000000..16a4342 Binary files /dev/null and b/minesweeper/images/cup.png differ diff --git a/minesweeper/images/flag.png b/minesweeper/images/flag.png new file mode 100755 index 0000000..5c008b0 Binary files /dev/null and b/minesweeper/images/flag.png differ diff --git a/minesweeper/images/fruit.png b/minesweeper/images/fruit.png new file mode 100755 index 0000000..298305a Binary files /dev/null and b/minesweeper/images/fruit.png differ diff --git a/minesweeper/images/hamburger.png b/minesweeper/images/hamburger.png new file mode 100755 index 0000000..e69df02 Binary files /dev/null and b/minesweeper/images/hamburger.png differ diff --git a/minesweeper/images/ice-cream-sprinkles.png b/minesweeper/images/ice-cream-sprinkles.png new file mode 100755 index 0000000..ccabcd6 Binary files /dev/null and b/minesweeper/images/ice-cream-sprinkles.png differ diff --git a/minesweeper/images/mushroom.png b/minesweeper/images/mushroom.png new file mode 100755 index 0000000..122887b Binary files /dev/null and b/minesweeper/images/mushroom.png differ diff --git a/minesweeper/images/plus.png b/minesweeper/images/plus.png new file mode 100755 index 0000000..195673c Binary files /dev/null and b/minesweeper/images/plus.png differ diff --git a/minesweeper/images/rocket.png b/minesweeper/images/rocket.png new file mode 100755 index 0000000..c0858fe Binary files /dev/null and b/minesweeper/images/rocket.png differ diff --git a/minesweeper/images/smiley-lol.png b/minesweeper/images/smiley-lol.png new file mode 100755 index 0000000..040dfb6 Binary files /dev/null and b/minesweeper/images/smiley-lol.png differ diff --git a/minesweeper/images/smiley.png b/minesweeper/images/smiley.png new file mode 100755 index 0000000..d2bf8a4 Binary files /dev/null and b/minesweeper/images/smiley.png differ diff --git a/minesweeper/minesweeper.py b/minesweeper/minesweeper.py new file mode 100644 index 0000000..ab213fc --- /dev/null +++ b/minesweeper/minesweeper.py @@ -0,0 +1,309 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * + +import random +import time + +IMG_BOMB = QImage("./images/bug.png") +IMG_FLAG = QImage("./images/flag.png") +IMG_START = QImage("./images/rocket.png") +IMG_CLOCK = QImage("./images/clock-select.png") + +NUM_COLORS = { + 1: QColor('#f44336'), + 2: QColor('#9C27B0'), + 3: QColor('#3F51B5'), + 4: QColor('#03A9F4'), + 5: QColor('#00BCD4'), + 6: QColor('#4CAF50'), + 7: QColor('#E91E63'), + 8: QColor('#FF9800') +} + +LEVELS = [ + (8, 10), + (16, 40), + (24, 99) +] + +STATUS_READY = 0 +STATUS_PLAYING = 1 +STATUS_FAILED = 2 +STATUS_SUCCESS = 3 + +STATUS_ICONS = { + STATUS_READY: "./images/plus.png", + STATUS_PLAYING: "./images/smiley.png", + STATUS_FAILED: "./images/cross.png", + STATUS_SUCCESS: "./images/smiley-lol.png", +} + + +class Pos(QWidget): + expandable = pyqtSignal(int, int) + clicked = pyqtSignal() + ohno = pyqtSignal() + + def __init__(self, x, y, *args, **kwargs): + super(Pos, self).__init__(*args, **kwargs) + + self.setFixedSize(QSize(20, 20)) + + self.x = x + self.y = y + + def reset(self): + self.is_start = False + self.is_mine = False + self.adjacent_n = 0 + + self.is_revealed = False + self.is_flagged = False + + self.update() + + def paintEvent(self, event): + p = QPainter(self) + p.setRenderHint(QPainter.Antialiasing) + + r = event.rect() + + if self.is_revealed: + color = self.palette().color(QPalette.Background) + outer, inner = color, color + else: + outer, inner = Qt.gray, Qt.lightGray + + p.fillRect(r, QBrush(inner)) + pen = QPen(outer) + pen.setWidth(1) + p.setPen(pen) + p.drawRect(r) + + if self.is_revealed: + if self.is_start: + p.drawPixmap(r, QPixmap(IMG_START)) + + elif self.is_mine: + p.drawPixmap(r, QPixmap(IMG_BOMB)) + + elif self.adjacent_n > 0: + pen = QPen(NUM_COLORS[self.adjacent_n]) + p.setPen(pen) + f = p.font() + f.setBold(True) + p.setFont(f) + p.drawText(r, Qt.AlignHCenter | Qt.AlignVCenter, str(self.adjacent_n)) + + elif self.is_flagged: + p.drawPixmap(r, QPixmap(IMG_FLAG)) + + def flag(self): + self.is_flagged = True + self.update() + + self.clicked.emit() + + def reveal(self): + self.is_revealed = True + self.update() + + def click(self): + if not self.is_revealed: + self.reveal() + if self.adjacent_n == 0: + self.expandable.emit(self.x, self.y) + + self.clicked.emit() + + def mouseReleaseEvent(self, e): + + if (e.button() == Qt.RightButton and not self.is_revealed): + self.flag() + + elif (e.button() == Qt.LeftButton): + self.click() + + if self.is_mine: + self.ohno.emit() + + +class MainWindow(QMainWindow): + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + self.b_size, self.n_mines = LEVELS[1] + + w = QWidget() + hb = QHBoxLayout() + + self.mines = QLabel() + self.mines.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + + self.clock = QLabel() + self.clock.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + + f = self.mines.font() + f.setPointSize(24) + f.setWeight(75) + self.mines.setFont(f) + self.clock.setFont(f) + + self._timer = QTimer() + self._timer.timeout.connect(self.update_timer) + self._timer.start(1000) # 1 second timer + + self.mines.setText("%03d" % self.n_mines) + self.clock.setText("000") + + self.button = QPushButton() + self.button.setFixedSize(QSize(32, 32)) + self.button.setIconSize(QSize(32, 32)) + self.button.setIcon(QIcon("./images/smiley.png")) + self.button.setFlat(True) + + self.button.pressed.connect(self.button_pressed) + + l = QLabel() + l.setPixmap(QPixmap.fromImage(IMG_BOMB)) + l.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + hb.addWidget(l) + + hb.addWidget(self.mines) + hb.addWidget(self.button) + hb.addWidget(self.clock) + + l = QLabel() + l.setPixmap(QPixmap.fromImage(IMG_CLOCK)) + l.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + hb.addWidget(l) + + vb = QVBoxLayout() + vb.addLayout(hb) + + self.grid = QGridLayout() + self.grid.setSpacing(5) + + vb.addLayout(self.grid) + w.setLayout(vb) + self.setCentralWidget(w) + + self.init_map() + self.update_status(STATUS_READY) + + self.reset_map() + self.update_status(STATUS_READY) + + self.show() + + def init_map(self): + # Add positions to the map + for x in range(0, self.b_size): + for y in range(0, self.b_size): + w = Pos(x, y) + self.grid.addWidget(w, y, x) + # Connect signal to handle expansion. + w.clicked.connect(self.trigger_start) + w.expandable.connect(self.expand_reveal) + w.ohno.connect(self.game_over) + + def reset_map(self): + # Clear all mine positions + for x in range(0, self.b_size): + for y in range(0, self.b_size): + w = self.grid.itemAtPosition(y, x).widget() + w.reset() + + # Add mines to the positions + positions = [] + while len(positions) < self.n_mines: + x, y = random.randint(0, self.b_size - 1), random.randint(0, self.b_size - 1) + if (x, y) not in positions: + w = self.grid.itemAtPosition(y, x).widget() + w.is_mine = True + positions.append((x, y)) + + def get_adjacency_n(x, y): + positions = self.get_surrounding(x, y) + n_mines = sum(1 if w.is_mine else 0 for w in positions) + + return n_mines + + # Add adjacencies to the positions + for x in range(0, self.b_size): + for y in range(0, self.b_size): + w = self.grid.itemAtPosition(y, x).widget() + w.adjacent_n = get_adjacency_n(x, y) + + # Place starting marker + while True: + x, y = random.randint(0, self.b_size - 1), random.randint(0, self.b_size - 1) + w = self.grid.itemAtPosition(y, x).widget() + # We don't want to start on a mine. + if (x, y) not in positions: + w = self.grid.itemAtPosition(y, x).widget() + w.is_start = True + + # Reveal all positions around this, if they are not mines either. + for w in self.get_surrounding(x, y): + if not w.is_mine: + w.click() + break + + def get_surrounding(self, x, y): + positions = [] + + for xi in range(max(0, x - 1), min(x + 2, self.b_size)): + for yi in range(max(0, y - 1), min(y + 2, self.b_size)): + positions.append(self.grid.itemAtPosition(yi, xi).widget()) + + return positions + + def button_pressed(self): + if self.status == STATUS_PLAYING: + self.update_status(STATUS_FAILED) + self.reveal_map() + + elif self.status == STATUS_FAILED: + self.update_status(STATUS_READY) + self.reset_map() + + def reveal_map(self): + for x in range(0, self.b_size): + for y in range(0, self.b_size): + w = self.grid.itemAtPosition(y, x).widget() + w.reveal() + + def expand_reveal(self, x, y): + for xi in range(max(0, x - 1), min(x + 2, self.b_size)): + for yi in range(max(0, y - 1), min(y + 2, self.b_size)): + w = self.grid.itemAtPosition(yi, xi).widget() + if not w.is_mine: + w.click() + + def trigger_start(self, *args): + if self.status != STATUS_PLAYING: + # First click. + self.update_status(STATUS_PLAYING) + # Start timer. + self._timer_start_nsecs = int(time.time()) + + def update_status(self, status): + self.status = status + self.button.setIcon(QIcon(STATUS_ICONS[self.status])) + + def update_timer(self): + if self.status == STATUS_PLAYING: + n_secs = int(time.time()) - self._timer_start_nsecs + self.clock.setText("%03d" % n_secs) + + def game_over(self): + self.reveal_map() + self.update_status(STATUS_FAILED) + + +if __name__ == '__main__': + app = QApplication([]) + window = MainWindow() + app.exec_() diff --git a/notepad/images/arrow-continue.png b/notepad/images/arrow-continue.png new file mode 100755 index 0000000..8034311 Binary files /dev/null and b/notepad/images/arrow-continue.png differ diff --git a/notepad/images/arrow-curve-180-left.png b/notepad/images/arrow-curve-180-left.png new file mode 100755 index 0000000..2a361a0 Binary files /dev/null and b/notepad/images/arrow-curve-180-left.png differ diff --git a/notepad/images/arrow-curve.png b/notepad/images/arrow-curve.png new file mode 100755 index 0000000..56776f5 Binary files /dev/null and b/notepad/images/arrow-curve.png differ diff --git a/notepad/images/blue-folder-open-document.png b/notepad/images/blue-folder-open-document.png new file mode 100755 index 0000000..6b5fab5 Binary files /dev/null and b/notepad/images/blue-folder-open-document.png differ diff --git a/notepad/images/clipboard-paste-document-text.png b/notepad/images/clipboard-paste-document-text.png new file mode 100755 index 0000000..08647f1 Binary files /dev/null and b/notepad/images/clipboard-paste-document-text.png differ diff --git a/notepad/images/disk--pencil.png b/notepad/images/disk--pencil.png new file mode 100755 index 0000000..47bf953 Binary files /dev/null and b/notepad/images/disk--pencil.png differ diff --git a/notepad/images/disk.png b/notepad/images/disk.png new file mode 100755 index 0000000..c619461 Binary files /dev/null and b/notepad/images/disk.png differ diff --git a/notepad/images/document-copy.png b/notepad/images/document-copy.png new file mode 100755 index 0000000..3836257 Binary files /dev/null and b/notepad/images/document-copy.png differ diff --git a/notepad/images/printer.png b/notepad/images/printer.png new file mode 100755 index 0000000..9edfbbc Binary files /dev/null and b/notepad/images/printer.png differ diff --git a/notepad/images/question.png b/notepad/images/question.png new file mode 100755 index 0000000..30a4703 Binary files /dev/null and b/notepad/images/question.png differ diff --git a/notepad/images/scissors.png b/notepad/images/scissors.png new file mode 100755 index 0000000..85f80b5 Binary files /dev/null and b/notepad/images/scissors.png differ diff --git a/notepad/images/selection-input.png b/notepad/images/selection-input.png new file mode 100755 index 0000000..18e2d70 Binary files /dev/null and b/notepad/images/selection-input.png differ diff --git a/notepad/images/ui-tab--plus.png b/notepad/images/ui-tab--plus.png new file mode 100755 index 0000000..de2fe4a Binary files /dev/null and b/notepad/images/ui-tab--plus.png differ diff --git a/notepad/notepad.py b/notepad/notepad.py new file mode 100644 index 0000000..8cd1bc1 --- /dev/null +++ b/notepad/notepad.py @@ -0,0 +1,190 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtPrintSupport import * + +import os +import sys + + +class MainWindow(QMainWindow): + + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + layout = QVBoxLayout() + self.editor = QPlainTextEdit() # Could also use a QTextEdit and set self.editor.setAcceptRichText(False) + + + # Setup the QTextEdit editor configuration + fixedfont = QFontDatabase.systemFont(QFontDatabase.FixedFont) + fixedfont.setPointSize(12) + self.editor.setFont(fixedfont) + + # self.path holds the path of the currently open file. + # If none, we haven't got a file open yet (or creating new). + self.path = None + + layout.addWidget(self.editor) + + container = QWidget() + container.setLayout(layout) + self.setCentralWidget(container) + + self.status = QStatusBar() + self.setStatusBar(self.status) + + file_toolbar = QToolBar("File") + file_toolbar.setIconSize(QSize(14, 14)) + self.addToolBar(file_toolbar) + file_menu = self.menuBar().addMenu("&File") + + open_file_action = QAction(QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) + open_file_action.setStatusTip("Open file") + open_file_action.triggered.connect(self.file_open) + file_menu.addAction(open_file_action) + file_toolbar.addAction(open_file_action) + + save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) + save_file_action.setStatusTip("Save current page") + save_file_action.triggered.connect(self.file_save) + file_menu.addAction(save_file_action) + file_toolbar.addAction(save_file_action) + + saveas_file_action = QAction(QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) + saveas_file_action.setStatusTip("Save current page to specified file") + saveas_file_action.triggered.connect(self.file_saveas) + file_menu.addAction(saveas_file_action) + file_toolbar.addAction(saveas_file_action) + + print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) + print_action.setStatusTip("Print current page") + print_action.triggered.connect(self.file_print) + file_menu.addAction(print_action) + file_toolbar.addAction(print_action) + + edit_toolbar = QToolBar("Edit") + edit_toolbar.setIconSize(QSize(16, 16)) + self.addToolBar(edit_toolbar) + edit_menu = self.menuBar().addMenu("&Edit") + + undo_action = QAction(QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) + undo_action.setStatusTip("Undo last change") + undo_action.triggered.connect(self.editor.undo) + edit_menu.addAction(undo_action) + + redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) + redo_action.setStatusTip("Redo last change") + redo_action.triggered.connect(self.editor.redo) + edit_toolbar.addAction(redo_action) + edit_menu.addAction(redo_action) + + edit_menu.addSeparator() + + cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) + cut_action.setStatusTip("Cut selected text") + cut_action.triggered.connect(self.editor.cut) + edit_toolbar.addAction(cut_action) + edit_menu.addAction(cut_action) + + copy_action = QAction(QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) + copy_action.setStatusTip("Copy selected text") + copy_action.triggered.connect(self.editor.copy) + edit_toolbar.addAction(copy_action) + edit_menu.addAction(copy_action) + + paste_action = QAction(QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) + paste_action.setStatusTip("Paste from clipboard") + paste_action.triggered.connect(self.editor.paste) + edit_toolbar.addAction(paste_action) + edit_menu.addAction(paste_action) + + select_action = QAction(QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) + select_action.setStatusTip("Select all text") + select_action.triggered.connect(self.editor.selectAll) + edit_menu.addAction(select_action) + + edit_menu.addSeparator() + + wrap_action = QAction(QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) + wrap_action.setStatusTip("Toggle wrap text to window") + wrap_action.setCheckable(True) + wrap_action.setChecked(True) + wrap_action.triggered.connect(self.edit_toggle_wrap) + edit_menu.addAction(wrap_action) + + self.update_title() + self.show() + + def dialog_critical(self, s): + dlg = QMessageBox(self) + dlg.setText(s) + dlg.setIcon(QMessageBox.Critical) + dlg.show() + + def file_open(self): + path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "Text documents (*.txt);All files (*.*)") + + try: + with open(path, 'rU') as f: + text = f.read() + + except Exception as e: + self.dialog_critical(str(e)) + + else: + self.path = path + self.editor.setPlainText(text) + self.update_title() + + def file_save(self): + if self.path is None: + # If we do not have a path, we need to use Save As. + return self.file_saveas() + + text = self.editor.toPlainText() + try: + with open(self.path, 'w') as f: + f.write(text) + + except Exception as e: + self.dialog_critical(str(e)) + + def file_saveas(self): + path, _ = QFileDialog.getSaveFileName(self, "Save file", "", "Text documents (*.txt);All files (*.*)") + text = self.editor.toPlainText() + + if not path: + # If dialog is cancelled, will return '' + return + + try: + with open(path, 'w') as f: + f.write(text) + + except Exception as e: + self.dialog_critical(str(e)) + + else: + self.path = path + self.update_title() + + def file_print(self): + dlg = QPrintDialog() + if dlg.exec_(): + self.editor.print_(dlg.printer()) + + def update_title(self): + self.setWindowTitle("%s - No2Pads" % (os.path.basename(self.path) if self.path else "Untitled")) + + def edit_toggle_wrap(self): + self.editor.setLineWrapMode( 1 if self.editor.lineWrapMode() == 0 else 0 ) + + +if __name__ == '__main__': + + app = QApplication(sys.argv) + app.setApplicationName("No2Pads") + + window = MainWindow() + app.exec_() \ No newline at end of file diff --git a/notes/MainWindow.py b/notes/MainWindow.py new file mode 100644 index 0000000..9d0a0ed --- /dev/null +++ b/notes/MainWindow.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'mainwindow.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(264, 279) + MainWindow.setAutoFillBackground(True) + self.centralWidget = QtWidgets.QWidget(MainWindow) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralWidget.sizePolicy().hasHeightForWidth()) + self.centralWidget.setSizePolicy(sizePolicy) + self.centralWidget.setObjectName("centralWidget") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralWidget) + self.verticalLayout_2.setContentsMargins(11, 11, 11, 11) + self.verticalLayout_2.setSpacing(6) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSpacing(6) + self.horizontalLayout.setObjectName("horizontalLayout") + self.closeButton = QtWidgets.QPushButton(self.centralWidget) + self.closeButton.setMinimumSize(QtCore.QSize(25, 20)) + self.closeButton.setMaximumSize(QtCore.QSize(25, 20)) + self.closeButton.setBaseSize(QtCore.QSize(2, 0)) + font = QtGui.QFont() + font.setPointSize(30) + font.setBold(True) + font.setWeight(75) + self.closeButton.setFont(font) + self.closeButton.setLayoutDirection(QtCore.Qt.LeftToRight) + self.closeButton.setStyleSheet("QPushButton {\n" +" border: 0px;\n" +"}") + self.closeButton.setObjectName("closeButton") + self.horizontalLayout.addWidget(self.closeButton) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.moreButton = QtWidgets.QPushButton(self.centralWidget) + self.moreButton.setMinimumSize(QtCore.QSize(25, 25)) + self.moreButton.setMaximumSize(QtCore.QSize(25, 25)) + font = QtGui.QFont() + font.setPointSize(30) + font.setBold(True) + font.setWeight(75) + self.moreButton.setFont(font) + self.moreButton.setStyleSheet("QPushButton {\n" +" border: 0px;\n" +"}") + self.moreButton.setObjectName("moreButton") + self.horizontalLayout.addWidget(self.moreButton) + self.verticalLayout.addLayout(self.horizontalLayout) + self.textEdit = QtWidgets.QTextEdit(self.centralWidget) + font = QtGui.QFont() + font.setPointSize(18) + self.textEdit.setFont(font) + self.textEdit.setFrameShape(QtWidgets.QFrame.NoFrame) + self.textEdit.setFrameShadow(QtWidgets.QFrame.Plain) + self.textEdit.setLineWidth(0) + self.textEdit.setObjectName("textEdit") + self.verticalLayout.addWidget(self.textEdit) + self.verticalLayout_2.addLayout(self.verticalLayout) + MainWindow.setCentralWidget(self.centralWidget) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Failamp")) + self.closeButton.setText(_translate("MainWindow", "×")) + self.moreButton.setText(_translate("MainWindow", "+")) + diff --git a/notes/images/cross-small.png b/notes/images/cross-small.png new file mode 100755 index 0000000..94af849 Binary files /dev/null and b/notes/images/cross-small.png differ diff --git a/notes/images/cross.png b/notes/images/cross.png new file mode 100755 index 0000000..6b9fa6d Binary files /dev/null and b/notes/images/cross.png differ diff --git a/notes/mainwindow.ui b/notes/mainwindow.ui new file mode 100644 index 0000000..7254e84 --- /dev/null +++ b/notes/mainwindow.ui @@ -0,0 +1,139 @@ + + + MainWindow + + + + 0 + 0 + 264 + 279 + + + + Failamp + + + true + + + + + 0 + 0 + + + + + + + + + + 25 + 20 + + + + + 25 + 20 + + + + + 2 + 0 + + + + + 30 + 75 + true + + + + Qt::LeftToRight + + + QPushButton { + border: 0px; +} + + + × + + + + + + + Qt::Horizontal + + + + 237 + 20 + + + + + + + + + 25 + 25 + + + + + 25 + 25 + + + + + 30 + 75 + true + + + + QPushButton { + border: 0px; +} + + + + + + + + + + + + + 18 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + + + + + diff --git a/notes/notes.db b/notes/notes.db new file mode 100644 index 0000000..31feea9 Binary files /dev/null and b/notes/notes.db differ diff --git a/notes/notes.py b/notes/notes.py new file mode 100644 index 0000000..a61535a --- /dev/null +++ b/notes/notes.py @@ -0,0 +1,121 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtMultimedia import * +from PyQt5.QtMultimediaWidgets import * + +from MainWindow import Ui_MainWindow + +from sqlalchemy import Column, ForeignKey, Integer, String +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy import create_engine + +Base = declarative_base() + +class Note(Base): + __tablename__ = 'note' + id = Column(Integer, primary_key=True) + text = Column(String(1000), nullable=False) + x = Column(Integer, nullable=False, default=0) + y = Column(Integer, nullable=False, default=0) + +engine = create_engine('sqlite:///notes.db') +# Initalize the database if it is not already. +#if not engine.dialect.has_table(engine, "note"): +Base.metadata.create_all(engine) + +# Create a session to handle updates. +Session = sessionmaker(bind=engine) +session = Session() + +_ACTIVE_NOTES = {} + +def create_new_note(): + MainWindow() + +class MainWindow(QMainWindow, Ui_MainWindow): + def __init__(self, *args, obj=None, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + self.setupUi(self) + self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) + self.show() + + # Load/save note data, store this notes db reference. + if obj: + self.obj = obj + self.load() + else: + self.obj = Note() + self.save() + + self.closeButton.pressed.connect(self.delete_window) + self.moreButton.pressed.connect(create_new_note) + self.textEdit.textChanged.connect(self.save) + + # Flags to store dragged-dropped + self._drag_active = False + + def load(self): + self.move(self.obj.x, self.obj.y) + self.textEdit.setHtml(self.obj.text) + _ACTIVE_NOTES[self.obj.id] = self + + def save(self): + self.obj.x = self.x() + self.obj.y = self.y() + self.obj.text = self.textEdit.toHtml() + session.add(self.obj) + session.commit() + _ACTIVE_NOTES[self.obj.id] = self + + def mousePressEvent(self, e): + self.previous_pos = e.globalPos() + + def mouseMoveEvent(self, e): + delta = e.globalPos() - self.previous_pos + self.move(self.x() + delta.x(), self.y()+delta.y()) + self.previous_pos = e.globalPos() + + self._drag_active = True + + def mouseReleaseEvent(self, e): + if self._drag_active: + self.save() + self._drag_active = False + + def delete_window(self): + result = QMessageBox.question(self, "Confirm delete", "Are you sure you want to delete this note?") + if result == QMessageBox.Yes: + session.delete(self.obj) + session.commit() + self.close() + + +if __name__ == '__main__': + app = QApplication([]) + app.setApplicationName("Brown Note") + app.setStyle("Fusion") + + # Custom brown palette. + palette = QPalette() + palette.setColor(QPalette.Window, QColor(188,170,164)) + palette.setColor(QPalette.WindowText, QColor(121,85,72)) + palette.setColor(QPalette.ButtonText, QColor(121,85,72)) + palette.setColor(QPalette.Text, QColor(121,85,72)) + palette.setColor(QPalette.Base, QColor(188,170,164)) + palette.setColor(QPalette.AlternateBase, QColor(188,170,164)) + app.setPalette(palette) + + existing_notes = session.query(Note).all() + if len(existing_notes) == 0: + MainWindow() + else: + for note in existing_notes: + MainWindow(obj=note) + + + + + + app.exec_() diff --git a/paint/MainWindow.py b/paint/MainWindow.py new file mode 100644 index 0000000..866ba05 --- /dev/null +++ b/paint/MainWindow.py @@ -0,0 +1,663 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'mainwindow.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(549, 452) + self.centralWidget = QtWidgets.QWidget(MainWindow) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralWidget.sizePolicy().hasHeightForWidth()) + self.centralWidget.setSizePolicy(sizePolicy) + self.centralWidget.setObjectName("centralWidget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.centralWidget) + self.verticalLayout.setContentsMargins(11, 11, 11, 11) + self.verticalLayout.setSpacing(6) + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) + self.horizontalLayout.setSpacing(6) + self.horizontalLayout.setObjectName("horizontalLayout") + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setSpacing(6) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.widget = QtWidgets.QWidget(self.centralWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth()) + self.widget.setSizePolicy(sizePolicy) + self.widget.setObjectName("widget") + self.gridLayout = QtWidgets.QGridLayout(self.widget) + self.gridLayout.setContentsMargins(11, 11, 11, 11) + self.gridLayout.setSpacing(15) + self.gridLayout.setObjectName("gridLayout") + self.rectButton = QtWidgets.QPushButton(self.widget) + self.rectButton.setMinimumSize(QtCore.QSize(30, 30)) + self.rectButton.setMaximumSize(QtCore.QSize(30, 30)) + self.rectButton.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("images/layer-shape.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.rectButton.setIcon(icon) + self.rectButton.setCheckable(True) + self.rectButton.setObjectName("rectButton") + self.gridLayout.addWidget(self.rectButton, 6, 0, 1, 1) + self.polylineButton = QtWidgets.QPushButton(self.widget) + self.polylineButton.setMinimumSize(QtCore.QSize(30, 30)) + self.polylineButton.setMaximumSize(QtCore.QSize(30, 30)) + self.polylineButton.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("images/layer-shape-polyline.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.polylineButton.setIcon(icon1) + self.polylineButton.setCheckable(True) + self.polylineButton.setObjectName("polylineButton") + self.gridLayout.addWidget(self.polylineButton, 5, 1, 1, 1) + self.selectrectButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.selectrectButton.sizePolicy().hasHeightForWidth()) + self.selectrectButton.setSizePolicy(sizePolicy) + self.selectrectButton.setMinimumSize(QtCore.QSize(30, 30)) + self.selectrectButton.setMaximumSize(QtCore.QSize(30, 30)) + self.selectrectButton.setText("") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("images/selection.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.selectrectButton.setIcon(icon2) + self.selectrectButton.setCheckable(True) + self.selectrectButton.setObjectName("selectrectButton") + self.gridLayout.addWidget(self.selectrectButton, 0, 1, 1, 1) + self.eraserButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.eraserButton.sizePolicy().hasHeightForWidth()) + self.eraserButton.setSizePolicy(sizePolicy) + self.eraserButton.setMinimumSize(QtCore.QSize(30, 30)) + self.eraserButton.setMaximumSize(QtCore.QSize(30, 30)) + self.eraserButton.setText("") + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap("images/eraser.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.eraserButton.setIcon(icon3) + self.eraserButton.setCheckable(True) + self.eraserButton.setObjectName("eraserButton") + self.gridLayout.addWidget(self.eraserButton, 1, 0, 1, 1) + self.stampButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.stampButton.sizePolicy().hasHeightForWidth()) + self.stampButton.setSizePolicy(sizePolicy) + self.stampButton.setMinimumSize(QtCore.QSize(30, 30)) + self.stampButton.setMaximumSize(QtCore.QSize(30, 30)) + self.stampButton.setText("") + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap("images/cake.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.stampButton.setIcon(icon4) + self.stampButton.setCheckable(True) + self.stampButton.setObjectName("stampButton") + self.gridLayout.addWidget(self.stampButton, 2, 1, 1, 1) + self.dropperButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.dropperButton.sizePolicy().hasHeightForWidth()) + self.dropperButton.setSizePolicy(sizePolicy) + self.dropperButton.setMinimumSize(QtCore.QSize(30, 30)) + self.dropperButton.setMaximumSize(QtCore.QSize(30, 30)) + self.dropperButton.setText("") + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap("images/pipette.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.dropperButton.setIcon(icon5) + self.dropperButton.setCheckable(True) + self.dropperButton.setObjectName("dropperButton") + self.gridLayout.addWidget(self.dropperButton, 2, 0, 1, 1) + self.selectpolyButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.selectpolyButton.sizePolicy().hasHeightForWidth()) + self.selectpolyButton.setSizePolicy(sizePolicy) + self.selectpolyButton.setMinimumSize(QtCore.QSize(30, 30)) + self.selectpolyButton.setMaximumSize(QtCore.QSize(30, 30)) + self.selectpolyButton.setText("") + icon6 = QtGui.QIcon() + icon6.addPixmap(QtGui.QPixmap("images/selection-poly.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.selectpolyButton.setIcon(icon6) + self.selectpolyButton.setCheckable(True) + self.selectpolyButton.setObjectName("selectpolyButton") + self.gridLayout.addWidget(self.selectpolyButton, 0, 0, 1, 1) + self.brushButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.brushButton.sizePolicy().hasHeightForWidth()) + self.brushButton.setSizePolicy(sizePolicy) + self.brushButton.setMinimumSize(QtCore.QSize(30, 30)) + self.brushButton.setMaximumSize(QtCore.QSize(30, 30)) + self.brushButton.setText("") + icon7 = QtGui.QIcon() + icon7.addPixmap(QtGui.QPixmap("images/paint-brush.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.brushButton.setIcon(icon7) + self.brushButton.setCheckable(True) + self.brushButton.setObjectName("brushButton") + self.gridLayout.addWidget(self.brushButton, 3, 1, 1, 1) + self.penButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.penButton.sizePolicy().hasHeightForWidth()) + self.penButton.setSizePolicy(sizePolicy) + self.penButton.setMinimumSize(QtCore.QSize(30, 30)) + self.penButton.setMaximumSize(QtCore.QSize(30, 30)) + self.penButton.setText("") + icon8 = QtGui.QIcon() + icon8.addPixmap(QtGui.QPixmap("images/pencil.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.penButton.setIcon(icon8) + self.penButton.setCheckable(True) + self.penButton.setObjectName("penButton") + self.gridLayout.addWidget(self.penButton, 3, 0, 1, 1) + self.fillButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fillButton.sizePolicy().hasHeightForWidth()) + self.fillButton.setSizePolicy(sizePolicy) + self.fillButton.setMinimumSize(QtCore.QSize(30, 30)) + self.fillButton.setMaximumSize(QtCore.QSize(30, 30)) + self.fillButton.setText("") + icon9 = QtGui.QIcon() + icon9.addPixmap(QtGui.QPixmap("images/paint-can.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.fillButton.setIcon(icon9) + self.fillButton.setCheckable(True) + self.fillButton.setObjectName("fillButton") + self.gridLayout.addWidget(self.fillButton, 1, 1, 1, 1) + self.textButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.textButton.sizePolicy().hasHeightForWidth()) + self.textButton.setSizePolicy(sizePolicy) + self.textButton.setMinimumSize(QtCore.QSize(30, 30)) + self.textButton.setMaximumSize(QtCore.QSize(30, 30)) + self.textButton.setText("") + icon10 = QtGui.QIcon() + icon10.addPixmap(QtGui.QPixmap("images/edit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.textButton.setIcon(icon10) + self.textButton.setCheckable(True) + self.textButton.setObjectName("textButton") + self.gridLayout.addWidget(self.textButton, 4, 1, 1, 1) + self.polygonButton = QtWidgets.QPushButton(self.widget) + self.polygonButton.setMinimumSize(QtCore.QSize(30, 30)) + self.polygonButton.setMaximumSize(QtCore.QSize(30, 30)) + self.polygonButton.setText("") + icon11 = QtGui.QIcon() + icon11.addPixmap(QtGui.QPixmap("images/layer-shape-polygon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.polygonButton.setIcon(icon11) + self.polygonButton.setCheckable(True) + self.polygonButton.setObjectName("polygonButton") + self.gridLayout.addWidget(self.polygonButton, 6, 1, 1, 1) + self.roundrectButton = QtWidgets.QPushButton(self.widget) + self.roundrectButton.setMinimumSize(QtCore.QSize(30, 30)) + self.roundrectButton.setMaximumSize(QtCore.QSize(30, 30)) + self.roundrectButton.setText("") + icon12 = QtGui.QIcon() + icon12.addPixmap(QtGui.QPixmap("images/layer-shape-round.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.roundrectButton.setIcon(icon12) + self.roundrectButton.setCheckable(True) + self.roundrectButton.setObjectName("roundrectButton") + self.gridLayout.addWidget(self.roundrectButton, 7, 1, 1, 1) + self.ellipseButton = QtWidgets.QPushButton(self.widget) + self.ellipseButton.setMinimumSize(QtCore.QSize(30, 30)) + self.ellipseButton.setMaximumSize(QtCore.QSize(30, 30)) + self.ellipseButton.setText("") + icon13 = QtGui.QIcon() + icon13.addPixmap(QtGui.QPixmap("images/layer-shape-ellipse.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.ellipseButton.setIcon(icon13) + self.ellipseButton.setCheckable(True) + self.ellipseButton.setObjectName("ellipseButton") + self.gridLayout.addWidget(self.ellipseButton, 7, 0, 1, 1) + self.lineButton = QtWidgets.QPushButton(self.widget) + self.lineButton.setMinimumSize(QtCore.QSize(30, 30)) + self.lineButton.setMaximumSize(QtCore.QSize(30, 30)) + self.lineButton.setText("") + icon14 = QtGui.QIcon() + icon14.addPixmap(QtGui.QPixmap("images/layer-shape-line.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.lineButton.setIcon(icon14) + self.lineButton.setCheckable(True) + self.lineButton.setObjectName("lineButton") + self.gridLayout.addWidget(self.lineButton, 5, 0, 1, 1) + self.sprayButton = QtWidgets.QPushButton(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.sprayButton.sizePolicy().hasHeightForWidth()) + self.sprayButton.setSizePolicy(sizePolicy) + self.sprayButton.setMinimumSize(QtCore.QSize(30, 30)) + self.sprayButton.setMaximumSize(QtCore.QSize(30, 30)) + self.sprayButton.setText("") + icon15 = QtGui.QIcon() + icon15.addPixmap(QtGui.QPixmap("images/spray.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.sprayButton.setIcon(icon15) + self.sprayButton.setCheckable(True) + self.sprayButton.setFlat(False) + self.sprayButton.setObjectName("sprayButton") + self.gridLayout.addWidget(self.sprayButton, 4, 0, 1, 1) + self.verticalLayout_2.addWidget(self.widget) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout_2.addItem(spacerItem) + self.horizontalLayout.addLayout(self.verticalLayout_2) + self.canvas = QtWidgets.QLabel(self.centralWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.canvas.sizePolicy().hasHeightForWidth()) + self.canvas.setSizePolicy(sizePolicy) + self.canvas.setText("") + self.canvas.setObjectName("canvas") + self.horizontalLayout.addWidget(self.canvas) + self.verticalLayout.addLayout(self.horizontalLayout) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setSpacing(6) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.widget_3 = QtWidgets.QWidget(self.centralWidget) + self.widget_3.setMinimumSize(QtCore.QSize(78, 0)) + self.widget_3.setMaximumSize(QtCore.QSize(78, 16777215)) + self.widget_3.setObjectName("widget_3") + self.secondaryButton = QtWidgets.QPushButton(self.widget_3) + self.secondaryButton.setGeometry(QtCore.QRect(30, 10, 40, 40)) + self.secondaryButton.setMinimumSize(QtCore.QSize(40, 40)) + self.secondaryButton.setMaximumSize(QtCore.QSize(40, 40)) + self.secondaryButton.setText("") + self.secondaryButton.setObjectName("secondaryButton") + self.primaryButton = QtWidgets.QPushButton(self.widget_3) + self.primaryButton.setGeometry(QtCore.QRect(10, 0, 40, 40)) + self.primaryButton.setMinimumSize(QtCore.QSize(40, 40)) + self.primaryButton.setMaximumSize(QtCore.QSize(40, 40)) + self.primaryButton.setText("") + self.primaryButton.setObjectName("primaryButton") + self.horizontalLayout_2.addWidget(self.widget_3) + self.widget_2 = QtWidgets.QWidget(self.centralWidget) + self.widget_2.setObjectName("widget_2") + self.gridLayout_2 = QtWidgets.QGridLayout(self.widget_2) + self.gridLayout_2.setContentsMargins(15, 0, 15, 15) + self.gridLayout_2.setSpacing(15) + self.gridLayout_2.setObjectName("gridLayout_2") + self.colorButton_11 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_11.sizePolicy().hasHeightForWidth()) + self.colorButton_11.setSizePolicy(sizePolicy) + self.colorButton_11.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_11.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_11.setText("") + self.colorButton_11.setObjectName("colorButton_11") + self.gridLayout_2.addWidget(self.colorButton_11, 0, 10, 1, 1) + self.colorButton_7 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_7.sizePolicy().hasHeightForWidth()) + self.colorButton_7.setSizePolicy(sizePolicy) + self.colorButton_7.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_7.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_7.setText("") + self.colorButton_7.setObjectName("colorButton_7") + self.gridLayout_2.addWidget(self.colorButton_7, 0, 6, 1, 1) + self.colorButton_9 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_9.sizePolicy().hasHeightForWidth()) + self.colorButton_9.setSizePolicy(sizePolicy) + self.colorButton_9.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_9.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_9.setText("") + self.colorButton_9.setObjectName("colorButton_9") + self.gridLayout_2.addWidget(self.colorButton_9, 0, 8, 1, 1) + self.colorButton_10 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_10.sizePolicy().hasHeightForWidth()) + self.colorButton_10.setSizePolicy(sizePolicy) + self.colorButton_10.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_10.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_10.setText("") + self.colorButton_10.setObjectName("colorButton_10") + self.gridLayout_2.addWidget(self.colorButton_10, 0, 9, 1, 1) + self.colorButton_23 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_23.sizePolicy().hasHeightForWidth()) + self.colorButton_23.setSizePolicy(sizePolicy) + self.colorButton_23.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_23.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_23.setText("") + self.colorButton_23.setObjectName("colorButton_23") + self.gridLayout_2.addWidget(self.colorButton_23, 1, 8, 1, 1) + self.colorButton_18 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_18.sizePolicy().hasHeightForWidth()) + self.colorButton_18.setSizePolicy(sizePolicy) + self.colorButton_18.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_18.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_18.setText("") + self.colorButton_18.setObjectName("colorButton_18") + self.gridLayout_2.addWidget(self.colorButton_18, 1, 3, 1, 1) + self.colorButton_20 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_20.sizePolicy().hasHeightForWidth()) + self.colorButton_20.setSizePolicy(sizePolicy) + self.colorButton_20.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_20.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_20.setText("") + self.colorButton_20.setObjectName("colorButton_20") + self.gridLayout_2.addWidget(self.colorButton_20, 1, 5, 1, 1) + self.colorButton_6 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_6.sizePolicy().hasHeightForWidth()) + self.colorButton_6.setSizePolicy(sizePolicy) + self.colorButton_6.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_6.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_6.setText("") + self.colorButton_6.setObjectName("colorButton_6") + self.gridLayout_2.addWidget(self.colorButton_6, 0, 5, 1, 1) + self.colorButton_3 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_3.sizePolicy().hasHeightForWidth()) + self.colorButton_3.setSizePolicy(sizePolicy) + self.colorButton_3.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_3.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_3.setText("") + self.colorButton_3.setObjectName("colorButton_3") + self.gridLayout_2.addWidget(self.colorButton_3, 0, 2, 1, 1) + self.colorButton_24 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_24.sizePolicy().hasHeightForWidth()) + self.colorButton_24.setSizePolicy(sizePolicy) + self.colorButton_24.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_24.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_24.setText("") + self.colorButton_24.setObjectName("colorButton_24") + self.gridLayout_2.addWidget(self.colorButton_24, 1, 9, 1, 1) + self.colorButton_17 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_17.sizePolicy().hasHeightForWidth()) + self.colorButton_17.setSizePolicy(sizePolicy) + self.colorButton_17.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_17.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_17.setText("") + self.colorButton_17.setObjectName("colorButton_17") + self.gridLayout_2.addWidget(self.colorButton_17, 1, 2, 1, 1) + self.colorButton_1 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_1.sizePolicy().hasHeightForWidth()) + self.colorButton_1.setSizePolicy(sizePolicy) + self.colorButton_1.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_1.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_1.setStyleSheet("") + self.colorButton_1.setText("") + self.colorButton_1.setObjectName("colorButton_1") + self.gridLayout_2.addWidget(self.colorButton_1, 0, 0, 1, 1) + self.colorButton_8 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_8.sizePolicy().hasHeightForWidth()) + self.colorButton_8.setSizePolicy(sizePolicy) + self.colorButton_8.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_8.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_8.setText("") + self.colorButton_8.setObjectName("colorButton_8") + self.gridLayout_2.addWidget(self.colorButton_8, 0, 7, 1, 1) + self.colorButton_27 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_27.sizePolicy().hasHeightForWidth()) + self.colorButton_27.setSizePolicy(sizePolicy) + self.colorButton_27.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_27.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_27.setText("") + self.colorButton_27.setObjectName("colorButton_27") + self.gridLayout_2.addWidget(self.colorButton_27, 1, 12, 1, 1) + self.colorButton_22 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_22.sizePolicy().hasHeightForWidth()) + self.colorButton_22.setSizePolicy(sizePolicy) + self.colorButton_22.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_22.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_22.setText("") + self.colorButton_22.setObjectName("colorButton_22") + self.gridLayout_2.addWidget(self.colorButton_22, 1, 7, 1, 1) + self.colorButton_15 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_15.sizePolicy().hasHeightForWidth()) + self.colorButton_15.setSizePolicy(sizePolicy) + self.colorButton_15.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_15.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_15.setText("") + self.colorButton_15.setObjectName("colorButton_15") + self.gridLayout_2.addWidget(self.colorButton_15, 1, 0, 1, 1) + self.colorButton_5 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_5.sizePolicy().hasHeightForWidth()) + self.colorButton_5.setSizePolicy(sizePolicy) + self.colorButton_5.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_5.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_5.setText("") + self.colorButton_5.setObjectName("colorButton_5") + self.gridLayout_2.addWidget(self.colorButton_5, 0, 4, 1, 1) + self.colorButton_2 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_2.sizePolicy().hasHeightForWidth()) + self.colorButton_2.setSizePolicy(sizePolicy) + self.colorButton_2.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_2.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_2.setText("") + self.colorButton_2.setObjectName("colorButton_2") + self.gridLayout_2.addWidget(self.colorButton_2, 0, 1, 1, 1) + self.colorButton_16 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_16.sizePolicy().hasHeightForWidth()) + self.colorButton_16.setSizePolicy(sizePolicy) + self.colorButton_16.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_16.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_16.setText("") + self.colorButton_16.setObjectName("colorButton_16") + self.gridLayout_2.addWidget(self.colorButton_16, 1, 1, 1, 1) + self.colorButton_14 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_14.sizePolicy().hasHeightForWidth()) + self.colorButton_14.setSizePolicy(sizePolicy) + self.colorButton_14.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_14.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_14.setText("") + self.colorButton_14.setObjectName("colorButton_14") + self.gridLayout_2.addWidget(self.colorButton_14, 0, 13, 1, 1) + self.colorButton_4 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_4.sizePolicy().hasHeightForWidth()) + self.colorButton_4.setSizePolicy(sizePolicy) + self.colorButton_4.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_4.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_4.setText("") + self.colorButton_4.setObjectName("colorButton_4") + self.gridLayout_2.addWidget(self.colorButton_4, 0, 3, 1, 1) + self.colorButton_21 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_21.sizePolicy().hasHeightForWidth()) + self.colorButton_21.setSizePolicy(sizePolicy) + self.colorButton_21.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_21.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_21.setText("") + self.colorButton_21.setObjectName("colorButton_21") + self.gridLayout_2.addWidget(self.colorButton_21, 1, 6, 1, 1) + self.colorButton_25 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_25.sizePolicy().hasHeightForWidth()) + self.colorButton_25.setSizePolicy(sizePolicy) + self.colorButton_25.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_25.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_25.setText("") + self.colorButton_25.setObjectName("colorButton_25") + self.gridLayout_2.addWidget(self.colorButton_25, 1, 10, 1, 1) + self.colorButton_12 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_12.sizePolicy().hasHeightForWidth()) + self.colorButton_12.setSizePolicy(sizePolicy) + self.colorButton_12.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_12.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_12.setText("") + self.colorButton_12.setObjectName("colorButton_12") + self.gridLayout_2.addWidget(self.colorButton_12, 0, 11, 1, 1) + self.colorButton_19 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_19.sizePolicy().hasHeightForWidth()) + self.colorButton_19.setSizePolicy(sizePolicy) + self.colorButton_19.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_19.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_19.setText("") + self.colorButton_19.setObjectName("colorButton_19") + self.gridLayout_2.addWidget(self.colorButton_19, 1, 4, 1, 1) + self.colorButton_13 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_13.sizePolicy().hasHeightForWidth()) + self.colorButton_13.setSizePolicy(sizePolicy) + self.colorButton_13.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_13.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_13.setText("") + self.colorButton_13.setObjectName("colorButton_13") + self.gridLayout_2.addWidget(self.colorButton_13, 0, 12, 1, 1) + self.colorButton_26 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_26.sizePolicy().hasHeightForWidth()) + self.colorButton_26.setSizePolicy(sizePolicy) + self.colorButton_26.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_26.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_26.setText("") + self.colorButton_26.setObjectName("colorButton_26") + self.gridLayout_2.addWidget(self.colorButton_26, 1, 11, 1, 1) + self.colorButton_28 = QtWidgets.QPushButton(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.colorButton_28.sizePolicy().hasHeightForWidth()) + self.colorButton_28.setSizePolicy(sizePolicy) + self.colorButton_28.setMinimumSize(QtCore.QSize(20, 20)) + self.colorButton_28.setMaximumSize(QtCore.QSize(20, 13)) + self.colorButton_28.setText("") + self.colorButton_28.setObjectName("colorButton_28") + self.gridLayout_2.addWidget(self.colorButton_28, 1, 13, 1, 1) + self.horizontalLayout_2.addWidget(self.widget_2) + self.stampchoiceButton = QtWidgets.QPushButton(self.centralWidget) + self.stampchoiceButton.setMinimumSize(QtCore.QSize(78, 55)) + self.stampchoiceButton.setMaximumSize(QtCore.QSize(78, 55)) + self.stampchoiceButton.setText("") + self.stampchoiceButton.setObjectName("stampchoiceButton") + self.horizontalLayout_2.addWidget(self.stampchoiceButton) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem1) + self.verticalLayout.addLayout(self.horizontalLayout_2) + MainWindow.setCentralWidget(self.centralWidget) + self.menuBar = QtWidgets.QMenuBar(MainWindow) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 549, 22)) + self.menuBar.setObjectName("menuBar") + self.menuFIle = QtWidgets.QMenu(self.menuBar) + self.menuFIle.setObjectName("menuFIle") + self.menuEdit = QtWidgets.QMenu(self.menuBar) + self.menuEdit.setObjectName("menuEdit") + self.menuView = QtWidgets.QMenu(self.menuBar) + self.menuView.setObjectName("menuView") + self.menuImage = QtWidgets.QMenu(self.menuBar) + self.menuImage.setObjectName("menuImage") + self.menuColors = QtWidgets.QMenu(self.menuBar) + self.menuColors.setObjectName("menuColors") + self.menuHelp = QtWidgets.QMenu(self.menuBar) + self.menuHelp.setObjectName("menuHelp") + MainWindow.setMenuBar(self.menuBar) + self.statusBar = QtWidgets.QStatusBar(MainWindow) + self.statusBar.setObjectName("statusBar") + MainWindow.setStatusBar(self.statusBar) + self.actionCopy = QtWidgets.QAction(MainWindow) + self.actionCopy.setObjectName("actionCopy") + self.actionPaste = QtWidgets.QAction(MainWindow) + self.actionPaste.setObjectName("actionPaste") + self.menuEdit.addAction(self.actionCopy) + self.menuEdit.addAction(self.actionPaste) + self.menuBar.addAction(self.menuFIle.menuAction()) + self.menuBar.addAction(self.menuEdit.menuAction()) + self.menuBar.addAction(self.menuView.menuAction()) + self.menuBar.addAction(self.menuImage.menuAction()) + self.menuBar.addAction(self.menuColors.menuAction()) + self.menuBar.addAction(self.menuHelp.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Piecasso")) + self.menuFIle.setTitle(_translate("MainWindow", "FIle")) + self.menuEdit.setTitle(_translate("MainWindow", "Edit")) + self.menuView.setTitle(_translate("MainWindow", "View")) + self.menuImage.setTitle(_translate("MainWindow", "Image")) + self.menuColors.setTitle(_translate("MainWindow", "Colors")) + self.menuHelp.setTitle(_translate("MainWindow", "Help")) + self.actionCopy.setText(_translate("MainWindow", "Copy")) + self.actionCopy.setShortcut(_translate("MainWindow", "Ctrl+C")) + self.actionPaste.setText(_translate("MainWindow", "Paste")) + self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V")) + diff --git a/paint/images/cake.png b/paint/images/cake.png new file mode 100755 index 0000000..c3bb699 Binary files /dev/null and b/paint/images/cake.png differ diff --git a/paint/images/edit.png b/paint/images/edit.png new file mode 100755 index 0000000..6db18b4 Binary files /dev/null and b/paint/images/edit.png differ diff --git a/paint/images/eraser.png b/paint/images/eraser.png new file mode 100755 index 0000000..bc6a3fa Binary files /dev/null and b/paint/images/eraser.png differ diff --git a/paint/images/layer-shape-ellipse.png b/paint/images/layer-shape-ellipse.png new file mode 100755 index 0000000..a186a26 Binary files /dev/null and b/paint/images/layer-shape-ellipse.png differ diff --git a/paint/images/layer-shape-line.png b/paint/images/layer-shape-line.png new file mode 100755 index 0000000..bdf68ec Binary files /dev/null and b/paint/images/layer-shape-line.png differ diff --git a/paint/images/layer-shape-polygon.png b/paint/images/layer-shape-polygon.png new file mode 100755 index 0000000..28eba7d Binary files /dev/null and b/paint/images/layer-shape-polygon.png differ diff --git a/paint/images/layer-shape-polyline.png b/paint/images/layer-shape-polyline.png new file mode 100755 index 0000000..e4b697c Binary files /dev/null and b/paint/images/layer-shape-polyline.png differ diff --git a/paint/images/layer-shape-round.png b/paint/images/layer-shape-round.png new file mode 100755 index 0000000..24aa4ce Binary files /dev/null and b/paint/images/layer-shape-round.png differ diff --git a/paint/images/layer-shape.png b/paint/images/layer-shape.png new file mode 100755 index 0000000..7c7883c Binary files /dev/null and b/paint/images/layer-shape.png differ diff --git a/paint/images/magnifier-zoom.png b/paint/images/magnifier-zoom.png new file mode 100755 index 0000000..8fc2189 Binary files /dev/null and b/paint/images/magnifier-zoom.png differ diff --git a/paint/images/paint-brush.png b/paint/images/paint-brush.png new file mode 100755 index 0000000..260406c Binary files /dev/null and b/paint/images/paint-brush.png differ diff --git a/paint/images/paint-can.png b/paint/images/paint-can.png new file mode 100755 index 0000000..fe618fc Binary files /dev/null and b/paint/images/paint-can.png differ diff --git a/paint/images/pencil.png b/paint/images/pencil.png new file mode 100755 index 0000000..3ef2fa6 Binary files /dev/null and b/paint/images/pencil.png differ diff --git a/paint/images/pipette.png b/paint/images/pipette.png new file mode 100755 index 0000000..2e6966a Binary files /dev/null and b/paint/images/pipette.png differ diff --git a/paint/images/printer.png b/paint/images/printer.png new file mode 100755 index 0000000..9edfbbc Binary files /dev/null and b/paint/images/printer.png differ diff --git a/paint/images/selection-poly.png b/paint/images/selection-poly.png new file mode 100644 index 0000000..fecaac2 Binary files /dev/null and b/paint/images/selection-poly.png differ diff --git a/paint/images/selection.png b/paint/images/selection.png new file mode 100755 index 0000000..02118fa Binary files /dev/null and b/paint/images/selection.png differ diff --git a/paint/images/spray.png b/paint/images/spray.png new file mode 100755 index 0000000..feb8ed7 Binary files /dev/null and b/paint/images/spray.png differ diff --git a/paint/images/stamp.png b/paint/images/stamp.png new file mode 100755 index 0000000..9d556de Binary files /dev/null and b/paint/images/stamp.png differ diff --git a/paint/mainwindow.ui b/paint/mainwindow.ui new file mode 100644 index 0000000..3953d85 --- /dev/null +++ b/paint/mainwindow.ui @@ -0,0 +1,1452 @@ + + + MainWindow + + + + 0 + 0 + 549 + 452 + + + + Piecasso + + + + + 0 + 0 + + + + + + + QLayout::SetDefaultConstraint + + + + + + + + 0 + 0 + + + + + 15 + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/layer-shape.pngimages/layer-shape.png + + + true + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/layer-shape-polyline.pngimages/layer-shape-polyline.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/selection.pngimages/selection.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/eraser.pngimages/eraser.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/cake.pngimages/cake.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/pipette.pngimages/pipette.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/selection-poly.pngimages/selection-poly.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/paint-brush.pngimages/paint-brush.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/pencil.pngimages/pencil.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/paint-can.pngimages/paint-can.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/edit.pngimages/edit.png + + + true + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/layer-shape-polygon.pngimages/layer-shape-polygon.png + + + true + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/layer-shape-round.pngimages/layer-shape-round.png + + + true + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/layer-shape-ellipse.pngimages/layer-shape-ellipse.png + + + true + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/layer-shape-line.pngimages/layer-shape-line.png + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + + images/spray.pngimages/spray.png + + + true + + + false + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + 78 + 0 + + + + + 78 + 16777215 + + + + + + 30 + 10 + 40 + 40 + + + + + 40 + 40 + + + + + 40 + 40 + + + + + + + + + + 10 + 0 + 40 + 40 + + + + + 40 + 40 + + + + + 40 + 40 + + + + + + + + + + + + + 15 + + + 0 + + + 15 + + + 15 + + + 15 + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + 20 + 13 + + + + + + + + + + + + + + + 78 + 55 + + + + + 78 + 55 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + 549 + 22 + + + + + FIle + + + + + Edit + + + + + + + View + + + + + Image + + + + + Colors + + + + + Help + + + + + + + + + + + + + Copy + + + Ctrl+C + + + + + Paste + + + Ctrl+V + + + + + + + diff --git a/paint/paint.py b/paint/paint.py new file mode 100644 index 0000000..bcf0808 --- /dev/null +++ b/paint/paint.py @@ -0,0 +1,624 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * + +QPushButton.pressed_right = pyqtSignal() + + +from MainWindow import Ui_MainWindow + +import os +import random +import types + + +SPRAY_PAINT_SD = 10 +SPRAY_PAINT_N = 100 + +COLORS = [ + '#000000', '#82817f', '#820300', '#868417', '#007e03', '#037e7b', '#040079', + '#81067a', '#7f7e45', '#05403c', '#0a7cf6', '#093c7e', '#7e07f9', '#7c4002', + + '#ffffff', '#c1c1c1', '#f70406', '#fffd00', '#08fb01', '#0bf8ee', '#0000fa', + '#b92fc2', '#fffc91', '#00fd83', '#87f9f9', '#8481c4', '#dc137d', '#fb803c', +] + +MODES = [ + 'selectpoly', 'selectrect', + 'eraser', 'fill', + 'dropper', 'stamp', + 'pen', 'brush', + 'spray', 'text', + 'line', 'polyline', + 'rect', 'polygon', + 'ellipse', 'roundrect' +] + +STAMP_DIR = './stamps' +STAMPS = [os.path.join(STAMP_DIR, f) for f in os.listdir(STAMP_DIR)] + +SELECTION_PEN = QPen(QColor(0xff, 0xff, 0xff), 1, Qt.DashLine) +PREVIEW_PEN = QPen(QColor(0xff, 0xff, 0xff), 1, Qt.SolidLine) + +class Canvas(QLabel): + + mode = 'rectangle' + + primary_color = QColor(Qt.black) + secondary_color = None + background_color = QColor(Qt.white) + + primary_color_updated = pyqtSignal(str) + secondary_color_updated = pyqtSignal(str) + + active_color = None + + timer_event = None + + + + def reset(self): + # Create the pixmap for display. + self.setPixmap(QPixmap(600,400)) + self.eraser_color = self.secondary_color or QColor(Qt.white) + self.eraser_color.setAlpha(100) + self.pixmap().fill(self.background_color) + + def set_primary_color(self, hex): + self.primary_color = QColor(hex) + + def set_secondary_color(self, hex): + self.secondary_color = QColor(hex) + + def set_mode(self, mode): + # Clean up active timer animations. + self.timer_cleanup() + # Reset mode-specific vars (all) + self.active_shape_fn = None + self.active_shape_args = () + self.last_pos = None + self.current_pos = None + self.origin_pos = None + self.history_pos = None + self.dash_offset = 0 + self.locked = False + # Apply the mode + self.mode = mode + + def reset_mode(self): + self.set_mode(self.mode) + + def on_timer(self): + if self.timer_event: + self.timer_event() + + def timer_cleanup(self): + if self.timer_event: + # Stop the timer, then trigger cleanup. + timer_event = self.timer_event + self.timer_event = None + timer_event(final=True) + + # Mouse events. + + def mousePressEvent(self, e): + fn = getattr(self, "%s_mousePressEvent" % self.mode, None) + if fn: + return fn(e) + + def mouseMoveEvent(self, e): + fn = getattr(self, "%s_mouseMoveEvent" % self.mode, None) + if fn: + return fn(e) + + def mouseReleaseEvent(self, e): + fn = getattr(self, "%s_mouseReleaseEvent" % self.mode, None) + if fn: + return fn(e) + + def mouseDoubleClickEvent(self, e): + fn = getattr(self, "%s_mouseDoubleClickEvent" % self.mode, None) + if fn: + return fn(e) + + # Generic events (shared by brush-like tools) + + def generic_mousePressEvent(self, e): + self.last_pos = e.pos() + + if e.button() == Qt.LeftButton: + self.active_color = self.primary_color + else: + self.active_color = self.secondary_color + + def generic_mouseReleaseEvent(self, e): + self.last_pos = None + + # Mode-specific events. + + # Eraser events + + def eraser_mousePressEvent(self, e): + return self.generic_mousePressEvent(e) + + def eraser_mouseMoveEvent(self, e): + if self.last_pos: + p = QPainter(self.pixmap()) + p.setPen(QPen(self.eraser_color, 30, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) + p.drawLine(self.last_pos, e.pos()) + + self.last_pos = e.pos() + self.update() + + def eraser_mouseReleaseEvent(self, e): + return self.generic_mouseReleaseEvent(e) + + # Stamp (pie) events + + def stamp_mousePressEvent(self, e): + p = QPainter(self.pixmap()) + stamp = QPixmap(random.choice(STAMPS)) + p.drawPixmap(e.x() - stamp.width() // 2, e.y() - stamp.height() // 2, stamp) + self.update() + + # Pen events + + def pen_mousePressEvent(self, e): + return self.generic_mousePressEvent(e) + + def pen_mouseMoveEvent(self, e): + if self.last_pos: + p = QPainter(self.pixmap()) + p.setPen(QPen(self.active_color, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) + + p.drawLine(self.last_pos, e.pos()) + + self.last_pos = e.pos() + self.update() + + def pen_mouseReleaseEvent(self, e): + return self.generic_mouseReleaseEvent(e) + + # Brush events + + def brush_mousePressEvent(self, e): + return self.generic_mousePressEvent(e) + + def brush_mouseMoveEvent(self, e): + if self.last_pos: + p = QPainter(self.pixmap()) + p.setPen(QPen(self.active_color, 10, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) + + p.drawLine(self.last_pos, e.pos()) + + self.last_pos = e.pos() + self.update() + + def brush_mouseReleaseEvent(self, e): + return self.generic_mouseReleaseEvent(e) + + # Spray events + + def spray_mousePressEvent(self, e): + return self.generic_mousePressEvent(e) + + def spray_mouseMoveEvent(self, e): + if self.last_pos: + p = QPainter(self.pixmap()) + p.setPen(QPen(self.active_color, 1)) + + for n in range(SPRAY_PAINT_N): + xo, yo = random.gauss(0, SPRAY_PAINT_SD), random.gauss(0, SPRAY_PAINT_SD) + p.drawPoint(e.x() + xo, e.y() + yo) + + self.update() + + def spray_mouseReleaseEvent(self, e): + return self.generic_mouseReleaseEvent(e) + + # Fill events + + def fill_mousePressEvent(self, e): + + if e.button() == Qt.LeftButton: + self.active_color = self.primary_color + else: + self.active_color = self.secondary_color + + # Convert to image for pixel-by-pixel reading. + image = self.pixmap().toImage() + w, h = image.width(), image.height() + s = image.bits().asstring(w * h * 4) + + x, y = e.x(), e.y() + # Lookup the 3-byte value at our current location. + i = (x + (y * w)) * 4 + target_color = s[i:i+3] + + # Convert bytestring to 1byte pp. true/false for matching colour. True values + # will be 255, non-matching 0. Simplifies the lookup in get_pixel and + # comparison in the main loop giving slight performance increase. + s = b''.join(b'\xff' if s[n:n+3] == target_color else b'\x00' for n in range(0, len(s), 4)) + + def get_pixel(x, y): + i = (x + (y * w)) + return s[i] + + have_seen = set() + to_fill = [] + queue = [(x, y)] + + def get_cardinal_points(have_seen, center_pos): + points = [] + cx, cy = center_pos + for x, y in [(1, 0), (0, 1), (-1, 0), (0, -1)]: + xx, yy = cx + x, cy + y + if (xx >= 0 and xx < w and + yy >= 0 and yy < h and + (xx, yy) not in have_seen): + + points.append((xx, yy)) + have_seen.add((xx, yy)) + + return points + + while queue: + x, y = queue.pop() + if get_pixel(x, y): # 255 for a match (True) or 0 for a miss (False) + to_fill.append((x,y)) + queue.extend(get_cardinal_points(have_seen, (x, y))) + + if to_fill: + # Now we have the points, perform the fill. + p = QPainter(self.pixmap()) + p.setPen(QPen(self.active_color)) + p.drawPoints(*[QPoint(*xy) for xy in to_fill]) + self.update() + + # Dropper events + + def dropper_mousePressEvent(self, e): + c = self.pixmap().toImage().pixel(e.pos()) + hex = QColor(c).name() + + if e.button() == Qt.LeftButton: + self.set_primary_color(hex) + self.primary_color_updated.emit(hex) # Update UI. + + elif e.button() == Qt.RightButton: + self.set_secondary_color(hex) + self.secondary_color_updated.emit(hex) # Update UI. + + # Select rectangle events + + def selectrect_mousePressEvent(self, e): + if self.current_pos: + # Clear up indicator. + self.timer_cleanup() + self.reset_mode() + + self.origin_pos = e.pos() + self.current_pos = e.pos() + self.timer_event = self.selectrect_timerEvent + + def selectrect_timerEvent(self, final=False): + p = QPainter(self.pixmap()) + p.setCompositionMode(QPainter.RasterOp_SourceXorDestination) + pen = QPen(QColor(0xff, 0xff, 0xff), 1, Qt.DashLine) + pen.setDashOffset(self.dash_offset) + p.setPen(pen) + if self.last_pos: + p.drawRect(QRect(self.origin_pos, self.last_pos)) + + self.dash_offset -= 1 + if not final: + pen.setDashOffset(self.dash_offset) + p.setPen(pen) + p.drawRect(QRect(self.origin_pos, self.current_pos)) + + self.update() + self.last_pos = self.current_pos + + def selectrect_mouseMoveEvent(self, e): + if self.locked == False: + self.current_pos = e.pos() + + def selectrect_mouseReleaseEvent(self, e): + self.current_pos = e.pos() + self.locked = True + + def selectrect_copy(self): + return self.pixmap().copy(QRect(self.origin_pos, self.current_pos)) + + # Generic shape events: Rectangle & Ellipse + + def generic_shape_mousePressEvent(self, e): + self.origin_pos = e.pos() + self.current_pos = e.pos() + self.timer_event = self.generic_shape_timerEvent + + def generic_shape_timerEvent(self, final=False): + p = QPainter(self.pixmap()) + p.setCompositionMode(QPainter.RasterOp_SourceXorDestination) + pen = PREVIEW_PEN + p.setPen(pen) + if self.last_pos: + getattr(p, self.active_shape_fn)(QRect(self.origin_pos, self.last_pos), *self.active_shape_args) + + if not final: + getattr(p, self.active_shape_fn)(QRect(self.origin_pos, self.current_pos), *self.active_shape_args) + + self.update() + self.last_pos = self.current_pos + + def generic_shape_mouseMoveEvent(self, e): + self.current_pos = e.pos() + + def generic_shape_mouseReleaseEvent(self, e): + if self.last_pos: + # Clear up indicator. + self.timer_cleanup() + + p = QPainter(self.pixmap()) + p.setPen(QPen(self.primary_color, 5, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin)) + + if self.secondary_color: + p.setBrush(QBrush(self.secondary_color)) + getattr(p, self.active_shape_fn)(QRect(self.origin_pos, e.pos()), *self.active_shape_args) + self.update() + + self.reset_mode() + + + # Line events + + def line_mousePressEvent(self, e): + self.origin_pos = e.pos() + self.current_pos = e.pos() + self.timer_event = self.line_timerEvent + + def line_timerEvent(self, final=False): + p = QPainter(self.pixmap()) + p.setCompositionMode(QPainter.RasterOp_SourceXorDestination) + pen = PREVIEW_PEN + p.setPen(pen) + if self.last_pos: + p.drawLine(self.origin_pos, self.last_pos) + + if not final: + p.drawLine(self.origin_pos, self.current_pos) + + self.update() + self.last_pos = self.current_pos + + def line_mouseMoveEvent(self, e): + self.current_pos = e.pos() + + def line_mouseReleaseEvent(self, e): + if self.last_pos: + # Clear up indicator. + self.timer_cleanup() + + p = QPainter(self.pixmap()) + p.setPen(QPen(self.primary_color, 5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) + + p.drawLine(self.origin_pos, e.pos()) + self.update() + + self.reset_mode() + + # Generic poly events + def generic_poly_mousePressEvent(self, e): + if e.button() == Qt.LeftButton: + if self.history_pos: + self.history_pos.append(e.pos()) + else: + self.history_pos = [e.pos()] + self.current_pos = e.pos() + self.timer_event = self.generic_poly_timerEvent + + if e.button() == Qt.RightButton and self.history_pos: + # Clean up, we're not drawing + self.timer_cleanup() + self.reset_mode() + + def generic_poly_timerEvent(self, final=False): + p = QPainter(self.pixmap()) + p.setCompositionMode(QPainter.RasterOp_SourceXorDestination) + pen = PREVIEW_PEN + p.setPen(pen) + if self.last_pos: + getattr(p, self.active_shape_fn)(*self.history_pos + [self.last_pos]) + + if not final: + getattr(p, self.active_shape_fn)(*self.history_pos + [self.current_pos]) + + self.update() + self.last_pos = self.current_pos + + def generic_poly_mouseMoveEvent(self, e): + self.current_pos = e.pos() + + def generic_poly_mouseDoubleClickEvent(self, e): + self.timer_cleanup() + p = QPainter(self.pixmap()) + p.setPen(QPen(self.primary_color, 5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) + + # Note the brush is ignored for polylines. + if self.secondary_color: + p.setBrush(QBrush(self.secondary_color)) + + getattr(p, self.active_shape_fn)(*self.history_pos + [e.pos()]) + self.update() + self.reset_mode() + + # Polyline events + + def polyline_mousePressEvent(self, e): + self.active_shape_fn = 'drawPolyline' + self.generic_poly_mousePressEvent(e) + + def polyline_timerEvent(self, final=False): + self.generic_poly_timerEvent(final) + + def polyline_mouseMoveEvent(self, e): + self.generic_poly_mouseMoveEvent(e) + + def polyline_mouseDoubleClickEvent(self, e): + self.generic_poly_mouseDoubleClickEvent(e) + + # Rectangle events + + def rect_mousePressEvent(self, e): + self.active_shape_fn = 'drawRect' + self.active_shape_args = () + self.generic_shape_mousePressEvent(e) + + def rect_timerEvent(self, final=False): + self.generic_shape_timerEvent(final) + + def rect_mouseMoveEvent(self, e): + self.generic_shape_mouseMoveEvent(e) + + def rect_mouseReleaseEvent(self, e): + self.generic_shape_mouseReleaseEvent(e) + + # Polygon events + + def polygon_mousePressEvent(self, e): + self.active_shape_fn = 'drawPolygon' + self.generic_poly_mousePressEvent(e) + + def polygon_timerEvent(self, final=False): + self.generic_poly_timerEvent(final) + + def polygon_mouseMoveEvent(self, e): + self.generic_poly_mouseMoveEvent(e) + + def polygon_mouseDoubleClickEvent(self, e): + self.generic_poly_mouseDoubleClickEvent(e) + + # Ellipse events + + def ellipse_mousePressEvent(self, e): + self.active_shape_fn = 'drawEllipse' + self.active_shape_args = () + self.generic_shape_mousePressEvent(e) + + def ellipse_timerEvent(self, final=False): + self.generic_shape_timerEvent(final) + + def ellipse_mouseMoveEvent(self, e): + self.generic_shape_mouseMoveEvent(e) + + def ellipse_mouseReleaseEvent(self, e): + self.generic_shape_mouseReleaseEvent(e) + + # Roundedrect events + + def roundrect_mousePressEvent(self, e): + self.active_shape_fn = 'drawRoundedRect' + self.active_shape_args = (25, 25) + self.generic_shape_mousePressEvent(e) + + def roundrect_timerEvent(self, final=False): + self.generic_shape_timerEvent(final) + + def roundrect_mouseMoveEvent(self, e): + self.generic_shape_mouseMoveEvent(e) + + def roundrect_mouseReleaseEvent(self, e): + self.generic_shape_mouseReleaseEvent(e) + + +class MainWindow(QMainWindow, Ui_MainWindow): + + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + self.setupUi(self) + + # Replace canvas placeholder from QtDesigner. + self.horizontalLayout.removeWidget(self.canvas) + self.canvas = Canvas() + self.canvas.reset() + # We need to enable mouse tracking to follow the mouse without the button pressed. + self.canvas.setMouseTracking(True) + self.horizontalLayout.addWidget(self.canvas) + + # Setup the mode buttons + mode_group = QButtonGroup(self) + mode_group.setExclusive(True) + + for mode in MODES: + btn = getattr(self, '%sButton' % mode) + btn.pressed.connect(lambda mode=mode: self.canvas.set_mode(mode)) + mode_group.addButton(btn) + + # Setup the color selection buttons. + self.primaryButton.pressed.connect(lambda: self.choose_color(self.set_primary_color)) + self.secondaryButton.pressed.connect(lambda: self.choose_color(self.set_secondary_color)) + + # Initialize button colours. + for n, hex in enumerate(COLORS, 1): + btn = getattr(self, 'colorButton_%d' % n) + btn.setStyleSheet('QPushButton { background-color: %s; }' % hex) + btn.hex = hex # For use in the event below + + def patch_mousePressEvent(self_, e): + if e.button() == Qt.LeftButton: + self.set_primary_color(self_.hex) + + elif e.button() == Qt.RightButton: + self.set_secondary_color(self_.hex) + + btn.mousePressEvent = types.MethodType(patch_mousePressEvent, btn) + + # Setup up action signals + self.actionCopy.triggered.connect(self.copy_to_clipboard) + + # Initialize animation timer. + self.timer = QTimer() + self.timer.timeout.connect(self.canvas.on_timer) + self.timer.setInterval(100) + self.timer.start() + + # Setup to agree with Canvas. + self.set_primary_color('#000000') + self.set_secondary_color('#ffffff') + + # Signals for canvas-initiated color changes (dropper). + self.canvas.primary_color_updated.connect(self.set_primary_color) + self.canvas.secondary_color_updated.connect(self.set_secondary_color) + + self.show() + + def choose_color(self, callback): + dlg = QColorDialog() + if dlg.exec(): + callback( dlg.selectedColor().name() ) + + def set_primary_color(self, hex): + self.canvas.set_primary_color(hex) + self.primaryButton.setStyleSheet('QPushButton { background-color: %s; }' % hex) + + def set_secondary_color(self, hex): + self.canvas.set_secondary_color(hex) + self.secondaryButton.setStyleSheet('QPushButton { background-color: %s; }' % hex) + + def copy_to_clipboard(self): + clipboard = QApplication.clipboard() + + if self.canvas.mode == 'selectrect' and self.canvas.locked: + clipboard.setPixmap(self.canvas.selectrect_copy()) + + elif self.canvas.mode == 'selectpoly' and self.canvas.locked: + clipboard.setPixmap(self.canvas.selectpoly_copy()) + + else: + clipboard.setPixmap(self.canvas.pixmap()) + +if __name__ == '__main__': + + app = QApplication([]) + window = MainWindow() + app.exec_() \ No newline at end of file diff --git a/paint/stamps/pie-apple.png b/paint/stamps/pie-apple.png new file mode 100644 index 0000000..a35705b Binary files /dev/null and b/paint/stamps/pie-apple.png differ diff --git a/paint/stamps/pie-cherry.png b/paint/stamps/pie-cherry.png new file mode 100644 index 0000000..4021f3c Binary files /dev/null and b/paint/stamps/pie-cherry.png differ diff --git a/paint/stamps/pie-cherry2.png b/paint/stamps/pie-cherry2.png new file mode 100644 index 0000000..c7aba56 Binary files /dev/null and b/paint/stamps/pie-cherry2.png differ diff --git a/paint/stamps/pie-lemon.png b/paint/stamps/pie-lemon.png new file mode 100644 index 0000000..ad11358 Binary files /dev/null and b/paint/stamps/pie-lemon.png differ diff --git a/paint/stamps/pie-moon.png b/paint/stamps/pie-moon.png new file mode 100644 index 0000000..5a5c3b7 Binary files /dev/null and b/paint/stamps/pie-moon.png differ diff --git a/paint/stamps/pie-pork.png b/paint/stamps/pie-pork.png new file mode 100644 index 0000000..236cddb Binary files /dev/null and b/paint/stamps/pie-pork.png differ diff --git a/paint/stamps/pie-pumpkin.png b/paint/stamps/pie-pumpkin.png new file mode 100644 index 0000000..7b7bebd Binary files /dev/null and b/paint/stamps/pie-pumpkin.png differ diff --git a/paint/stamps/pie-walnut.png b/paint/stamps/pie-walnut.png new file mode 100644 index 0000000..b948ba7 Binary files /dev/null and b/paint/stamps/pie-walnut.png differ diff --git a/wordprocessor/images/arrow-continue.png b/wordprocessor/images/arrow-continue.png new file mode 100755 index 0000000..8034311 Binary files /dev/null and b/wordprocessor/images/arrow-continue.png differ diff --git a/wordprocessor/images/arrow-curve-180-left.png b/wordprocessor/images/arrow-curve-180-left.png new file mode 100755 index 0000000..2a361a0 Binary files /dev/null and b/wordprocessor/images/arrow-curve-180-left.png differ diff --git a/wordprocessor/images/arrow-curve.png b/wordprocessor/images/arrow-curve.png new file mode 100755 index 0000000..56776f5 Binary files /dev/null and b/wordprocessor/images/arrow-curve.png differ diff --git a/wordprocessor/images/blue-folder-open-document.png b/wordprocessor/images/blue-folder-open-document.png new file mode 100755 index 0000000..6b5fab5 Binary files /dev/null and b/wordprocessor/images/blue-folder-open-document.png differ diff --git a/wordprocessor/images/clipboard-paste-document-text.png b/wordprocessor/images/clipboard-paste-document-text.png new file mode 100755 index 0000000..08647f1 Binary files /dev/null and b/wordprocessor/images/clipboard-paste-document-text.png differ diff --git a/wordprocessor/images/disk--pencil.png b/wordprocessor/images/disk--pencil.png new file mode 100755 index 0000000..47bf953 Binary files /dev/null and b/wordprocessor/images/disk--pencil.png differ diff --git a/wordprocessor/images/disk.png b/wordprocessor/images/disk.png new file mode 100755 index 0000000..c619461 Binary files /dev/null and b/wordprocessor/images/disk.png differ diff --git a/wordprocessor/images/document-copy.png b/wordprocessor/images/document-copy.png new file mode 100755 index 0000000..3836257 Binary files /dev/null and b/wordprocessor/images/document-copy.png differ diff --git a/wordprocessor/images/edit-alignment-center.png b/wordprocessor/images/edit-alignment-center.png new file mode 100755 index 0000000..853497c Binary files /dev/null and b/wordprocessor/images/edit-alignment-center.png differ diff --git a/wordprocessor/images/edit-alignment-justify.png b/wordprocessor/images/edit-alignment-justify.png new file mode 100755 index 0000000..0a102d5 Binary files /dev/null and b/wordprocessor/images/edit-alignment-justify.png differ diff --git a/wordprocessor/images/edit-alignment-right.png b/wordprocessor/images/edit-alignment-right.png new file mode 100755 index 0000000..7af865d Binary files /dev/null and b/wordprocessor/images/edit-alignment-right.png differ diff --git a/wordprocessor/images/edit-alignment.png b/wordprocessor/images/edit-alignment.png new file mode 100755 index 0000000..f1cd36b Binary files /dev/null and b/wordprocessor/images/edit-alignment.png differ diff --git a/wordprocessor/images/edit-bold.png b/wordprocessor/images/edit-bold.png new file mode 100755 index 0000000..200b9cf Binary files /dev/null and b/wordprocessor/images/edit-bold.png differ diff --git a/wordprocessor/images/edit-color.png b/wordprocessor/images/edit-color.png new file mode 100755 index 0000000..676f1ab Binary files /dev/null and b/wordprocessor/images/edit-color.png differ diff --git a/wordprocessor/images/edit-italic.png b/wordprocessor/images/edit-italic.png new file mode 100755 index 0000000..18052c2 Binary files /dev/null and b/wordprocessor/images/edit-italic.png differ diff --git a/wordprocessor/images/edit-list-order.png b/wordprocessor/images/edit-list-order.png new file mode 100755 index 0000000..687b410 Binary files /dev/null and b/wordprocessor/images/edit-list-order.png differ diff --git a/wordprocessor/images/edit-list.png b/wordprocessor/images/edit-list.png new file mode 100755 index 0000000..2ad2087 Binary files /dev/null and b/wordprocessor/images/edit-list.png differ diff --git a/wordprocessor/images/edit-underline.png b/wordprocessor/images/edit-underline.png new file mode 100755 index 0000000..438f391 Binary files /dev/null and b/wordprocessor/images/edit-underline.png differ diff --git a/wordprocessor/images/printer.png b/wordprocessor/images/printer.png new file mode 100755 index 0000000..9edfbbc Binary files /dev/null and b/wordprocessor/images/printer.png differ diff --git a/wordprocessor/images/question.png b/wordprocessor/images/question.png new file mode 100755 index 0000000..30a4703 Binary files /dev/null and b/wordprocessor/images/question.png differ diff --git a/wordprocessor/images/scissors.png b/wordprocessor/images/scissors.png new file mode 100755 index 0000000..85f80b5 Binary files /dev/null and b/wordprocessor/images/scissors.png differ diff --git a/wordprocessor/images/selection-input.png b/wordprocessor/images/selection-input.png new file mode 100755 index 0000000..18e2d70 Binary files /dev/null and b/wordprocessor/images/selection-input.png differ diff --git a/wordprocessor/images/ui-tab--plus.png b/wordprocessor/images/ui-tab--plus.png new file mode 100755 index 0000000..de2fe4a Binary files /dev/null and b/wordprocessor/images/ui-tab--plus.png differ diff --git a/wordprocessor/wordprocessor.py b/wordprocessor/wordprocessor.py new file mode 100644 index 0000000..9039d73 --- /dev/null +++ b/wordprocessor/wordprocessor.py @@ -0,0 +1,378 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtPrintSupport import * + +import os +import sys +import uuid + +FONT_SIZES = [9, 10, 11, 12, 13, 14, 18, 24, 36, 48, 64, 72, 96, 144, 288] +IMAGE_EXTENSIONS = ['.jpg','.png','.bmp'] +HTML_EXTENSIONS = ['.htm', '.html'] + +def hexuuid(): + return uuid.uuid4().hex + +def splitext(p): + return os.path.splitext(p)[1].lower() + +class TextEdit(QTextEdit): + + def canInsertFromMimeData(self, source): + + if source.hasImage(): + return True + else: + return super(TextEdit, self).canInsertFromMimeData(source) + + def insertFromMimeData(self, source): + + cursor = self.textCursor() + document = self.document() + + if source.hasUrls(): + + for u in source.urls(): + file_ext = splitext(str(u.toLocalFile())) + if u.isLocalFile() and file_ext in IMAGE_EXTENSIONS: + image = QImage(u.toLocalFile()) + document.addResource(QTextDocument.ImageResource, u, image) + cursor.insertImage(u.toLocalFile()) + + else: + # If we hit a non-image or non-local URL break the loop and fall out + # to the super call & let Qt handle it + break + + else: + # If all were valid images, finish here. + return + + + elif source.hasImage(): + image = source.imageData() + uuid = hexuuid() + document.addResource(QTextDocument.ImageResource, uuid, image) + cursor.insertImage(uuid) + return + + super(TextEdit, self).insertFromMimeData(source) + + +class MainWindow(QMainWindow): + + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + layout = QVBoxLayout() + self.editor = TextEdit() + # Setup the QTextEdit editor configuration + self.editor.setAcceptRichText(False) + self.editor.setAutoFormatting(QTextEdit.AutoAll) + self.editor.selectionChanged.connect(self.update_format) + # Initialize default font size. + font = QFont('Times', 12) + self.editor.setFont(font) + # We need to repeat the size to init the current format. + self.editor.setFontPointSize(12) + + # self.path holds the path of the currently open file. + # If none, we haven't got a file open yet (or creating new). + self.path = None + + layout.addWidget(self.editor) + + container = QWidget() + container.setLayout(layout) + self.setCentralWidget(container) + + self.status = QStatusBar() + self.setStatusBar(self.status) + + # Uncomment to disable native menubar on Mac + # self.menuBar().setNativeMenuBar(False) + + file_toolbar = QToolBar("File") + file_toolbar.setIconSize(QSize(14, 14)) + self.addToolBar(file_toolbar) + file_menu = self.menuBar().addMenu("&File") + + open_file_action = QAction(QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) + open_file_action.setStatusTip("Open file") + open_file_action.triggered.connect(self.file_open) + file_menu.addAction(open_file_action) + file_toolbar.addAction(open_file_action) + + save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) + save_file_action.setStatusTip("Save current page") + save_file_action.triggered.connect(self.file_save) + file_menu.addAction(save_file_action) + file_toolbar.addAction(save_file_action) + + saveas_file_action = QAction(QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) + saveas_file_action.setStatusTip("Save current page to specified file") + saveas_file_action.triggered.connect(self.file_saveas) + file_menu.addAction(saveas_file_action) + file_toolbar.addAction(saveas_file_action) + + print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) + print_action.setStatusTip("Print current page") + print_action.triggered.connect(self.file_print) + file_menu.addAction(print_action) + file_toolbar.addAction(print_action) + + edit_toolbar = QToolBar("Edit") + edit_toolbar.setIconSize(QSize(16, 16)) + self.addToolBar(edit_toolbar) + edit_menu = self.menuBar().addMenu("&Edit") + + undo_action = QAction(QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) + undo_action.setStatusTip("Undo last change") + undo_action.triggered.connect(self.editor.undo) + edit_menu.addAction(undo_action) + + redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) + redo_action.setStatusTip("Redo last change") + redo_action.triggered.connect(self.editor.redo) + edit_toolbar.addAction(redo_action) + edit_menu.addAction(redo_action) + + edit_menu.addSeparator() + + cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) + cut_action.setStatusTip("Cut selected text") + cut_action.setShortcut(QKeySequence.Cut) + cut_action.triggered.connect(self.editor.cut) + edit_toolbar.addAction(cut_action) + edit_menu.addAction(cut_action) + + copy_action = QAction(QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) + copy_action.setStatusTip("Copy selected text") + cut_action.setShortcut(QKeySequence.Copy) + copy_action.triggered.connect(self.editor.copy) + edit_toolbar.addAction(copy_action) + edit_menu.addAction(copy_action) + + paste_action = QAction(QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) + paste_action.setStatusTip("Paste from clipboard") + cut_action.setShortcut(QKeySequence.Paste) + paste_action.triggered.connect(self.editor.paste) + edit_toolbar.addAction(paste_action) + edit_menu.addAction(paste_action) + + select_action = QAction(QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) + select_action.setStatusTip("Select all text") + cut_action.setShortcut(QKeySequence.SelectAll) + select_action.triggered.connect(self.editor.selectAll) + edit_menu.addAction(select_action) + + edit_menu.addSeparator() + + wrap_action = QAction(QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) + wrap_action.setStatusTip("Toggle wrap text to window") + wrap_action.setCheckable(True) + wrap_action.setChecked(True) + wrap_action.triggered.connect(self.edit_toggle_wrap) + edit_menu.addAction(wrap_action) + + format_toolbar = QToolBar("Format") + format_toolbar.setIconSize(QSize(16, 16)) + self.addToolBar(format_toolbar) + format_menu = self.menuBar().addMenu("&Format") + + # We need references to these actions/settings to update as selection changes, so attach to self. + self.fonts = QFontComboBox() + self.fonts.currentFontChanged.connect(self.editor.setCurrentFont) + format_toolbar.addWidget(self.fonts) + + self.fontsize = QComboBox() + self.fontsize.setEditable(True) + self.fontsize.addItems([str(s) for s in FONT_SIZES]) + + # Connect to the signal producing the text of the current selection. Convert the string to float + # and set as the pointsize. We could also use the index + retrieve from FONT_SIZES. + self.fontsize.currentIndexChanged[str].connect(lambda s: self.editor.setFontPointSize(float(s)) ) + format_toolbar.addWidget(self.fontsize) + + self.bold_action = QAction(QIcon(os.path.join('images', 'edit-bold.png')), "Bold", self) + self.bold_action.setStatusTip("Bold") + self.bold_action.setShortcut(QKeySequence.Bold) + self.bold_action.setCheckable(True) + self.bold_action.toggled.connect(lambda x: self.editor.setFontWeight(QFont.Bold if x else QFont.Normal)) + format_toolbar.addAction(self.bold_action) + format_menu.addAction(self.bold_action) + + self.italic_action = QAction(QIcon(os.path.join('images', 'edit-italic.png')), "Italic", self) + self.italic_action.setStatusTip("Italic") + self.italic_action.setShortcut(QKeySequence.Italic) + self.italic_action.setCheckable(True) + self.italic_action.toggled.connect(self.editor.setFontItalic) + format_toolbar.addAction(self.italic_action) + format_menu.addAction(self.italic_action) + + self.underline_action = QAction(QIcon(os.path.join('images', 'edit-underline.png')), "Underline", self) + self.underline_action.setStatusTip("Underline") + self.underline_action.setShortcut(QKeySequence.Underline) + self.underline_action.setCheckable(True) + self.underline_action.toggled.connect(self.editor.setFontUnderline) + format_toolbar.addAction(self.underline_action) + format_menu.addAction(self.underline_action) + + format_menu.addSeparator() + + self.alignl_action = QAction(QIcon(os.path.join('images', 'edit-alignment.png')), "Align left", self) + self.alignl_action.setStatusTip("Align text left") + self.alignl_action.setCheckable(True) + self.alignl_action.triggered.connect(lambda: self.editor.setAlignment(Qt.AlignLeft)) + format_toolbar.addAction(self.alignl_action) + format_menu.addAction(self.alignl_action) + + self.alignc_action = QAction(QIcon(os.path.join('images', 'edit-alignment-center.png')), "Align center", self) + self.alignc_action.setStatusTip("Align text center") + self.alignc_action.setCheckable(True) + self.alignc_action.triggered.connect(lambda: self.editor.setAlignment(Qt.AlignCenter)) + format_toolbar.addAction(self.alignc_action) + format_menu.addAction(self.alignc_action) + + self.alignr_action = QAction(QIcon(os.path.join('images', 'edit-alignment-right.png')), "Align right", self) + self.alignr_action.setStatusTip("Align text right") + self.alignr_action.setCheckable(True) + self.alignr_action.triggered.connect(lambda: self.editor.setAlignment(Qt.AlignRight)) + format_toolbar.addAction(self.alignr_action) + format_menu.addAction(self.alignr_action) + + self.alignj_action = QAction(QIcon(os.path.join('images', 'edit-alignment-justify.png')), "Justify", self) + self.alignj_action.setStatusTip("Justify text") + self.alignj_action.setCheckable(True) + self.alignj_action.triggered.connect(lambda: self.editor.setAlignment(Qt.AlignJustify)) + format_toolbar.addAction(self.alignj_action) + format_menu.addAction(self.alignj_action) + + format_group = QActionGroup(self) + format_group.setExclusive(True) + format_group.addAction(self.alignl_action) + format_group.addAction(self.alignc_action) + format_group.addAction(self.alignr_action) + format_group.addAction(self.alignj_action) + + format_menu.addSeparator() + + # A list of all format-related widgets/actions, so we can disable/enable signals when updating. + self._format_actions = [ + self.fonts, + self.fontsize, + self.bold_action, + self.italic_action, + self.underline_action, + # We don't need to disable signals for alignment, as they are paragraph-wide. + ] + + # Initialize. + self.update_format() + self.update_title() + self.show() + + def block_signals(self, objects, b): + for o in objects: + o.blockSignals(b) + + def update_format(self): + """ + Update the font format toolbar/actions when a new text selection is made. This is neccessary to keep + toolbars/etc. in sync with the current edit state. + :return: + """ + # Disable signals for all format widgets, so changing values here does not trigger further formatting. + self.block_signals(self._format_actions, True) + + self.fonts.setCurrentFont(self.editor.currentFont()) + # Nasty, but we get the font-size as a float but want it was an int + self.fontsize.setCurrentText(str(int(self.editor.fontPointSize()))) + + self.italic_action.setChecked(self.editor.fontItalic()) + self.underline_action.setChecked(self.editor.fontUnderline()) + self.bold_action.setChecked(self.editor.fontWeight() == QFont.Bold) + + self.alignl_action.setChecked(self.editor.alignment() == Qt.AlignLeft) + self.alignc_action.setChecked(self.editor.alignment() == Qt.AlignCenter) + self.alignr_action.setChecked(self.editor.alignment() == Qt.AlignRight) + self.alignj_action.setChecked(self.editor.alignment() == Qt.AlignJustify) + + self.block_signals(self._format_actions, False) + + def dialog_critical(self, s): + dlg = QMessageBox(self) + dlg.setText(s) + dlg.setIcon(QMessageBox.Critical) + dlg.show() + + def file_open(self): + path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "HTML documents (*.html);Text documents (*.txt);All files (*.*)") + + try: + with open(path, 'rU') as f: + text = f.read() + + except Exception as e: + self.dialog_critical(str(e)) + + else: + self.path = path + # Qt will automatically try and guess the format as txt/html + self.editor.setText(text) + self.update_title() + + def file_save(self): + if self.path is None: + # If we do not have a path, we need to use Save As. + return self.file_saveas() + + text = self.editor.toHtml() if splitext(self.path) in HTML_EXTENSIONS else self.editor.toPlainText() + + try: + with open(self.path, 'w') as f: + f.write(text) + + except Exception as e: + self.dialog_critical(str(e)) + + def file_saveas(self): + path, _ = QFileDialog.getSaveFileName(self, "Save file", "", "HTML documents (*.html);Text documents (*.txt);All files (*.*)") + + if not path: + # If dialog is cancelled, will return '' + return + + text = self.editor.toHtml() if splitext(path) in HTML_EXTENSIONS else self.editor.toPlainText() + + try: + with open(path, 'w') as f: + f.write(text) + + except Exception as e: + self.dialog_critical(str(e)) + + else: + self.path = path + self.update_title() + + def file_print(self): + dlg = QPrintDialog() + if dlg.exec_(): + self.editor.print_(dlg.printer()) + + def update_title(self): + self.setWindowTitle("%s - Megasolid Idiom" % (os.path.basename(self.path) if self.path else "Untitled")) + + def edit_toggle_wrap(self): + self.editor.setLineWrapMode( 1 if self.editor.lineWrapMode() == 0 else 0 ) + + +if __name__ == '__main__': + + app = QApplication(sys.argv) + app.setApplicationName("Megasolid Idiom") + + window = MainWindow() + app.exec_() \ No newline at end of file