diff --git a/.drone.yml b/.drone.yml index 538ea6e..3e22be3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -67,7 +67,7 @@ steps: key: from_secret: ssh_key target: /var/www/new-blog/ - source: /drone/src/resources/public/* + source: /drone/src/bases/do-blog/resources/public/* strip_components: 4 # - name: promote-deployed-site diff --git a/bases/do-blog/deps.edn b/bases/do-blog/deps.edn index aa4cb27..5eaf0ff 100644 --- a/bases/do-blog/deps.edn +++ b/bases/do-blog/deps.edn @@ -16,7 +16,9 @@ :sha "13e57f11da75516844922fe28bfd6aa7692b7c34"} } - :aliases {:dev {:extra-deps {thheller/shadow-cljs {:mvn/version "2.25.2"} + :aliases {:dev {:extra-paths + ["components/ui-hiccup/src"] + :extra-deps {thheller/shadow-cljs {:mvn/version "2.25.2"} cider/cider-nrepl {:mvn/version "0.50.2"} refactor-nrepl/refactor-nrepl {:mvn/version "3.10.0"} cider/piggieback {:mvn/version "0.5.3"}}} diff --git a/bases/do-blog/resources/documents/emacs/blogging-with-org-and-nikola.org b/bases/do-blog/resources/documents/emacs/blogging-with-org-and-nikola.org new file mode 100755 index 0000000..3c02a8d --- /dev/null +++ b/bases/do-blog/resources/documents/emacs/blogging-with-org-and-nikola.org @@ -0,0 +1,165 @@ +#+TITLE: Blogging with org mode and nikola +#+DATE: 2017-07-01 12:00:00 UTC +#+DESCRIPTION: How I use emacs and nikola to write my blog +#+FILETAGS: emacs:python +#+CATEGORY: emacs +#+SLUG: blogging-with-org-mode-and-nikola + +#+BEGIN_COMMENT +.. title: Blogging with org mode and nikola +.. slug: blogging-with-org-mode-and-nikola +.. date: 2017-07-01 12:00:00 UTC +.. tags: emacs, python +.. category: emacs +.. description: How I use emacs and nikola to write my blog +.. type: text +#+END_COMMENT + +Below are some tips and technologies used to write and deploy this blog. +** Getting started +To get started creating your blog first install nikola with pip or use the docker container, then run =nikola init= to create your new blog. +** Org header attributes +All org files should start with this header containing the blog entry meta data, the below example is from this post. +#+BEGIN_EXAMPLE +#+BEGIN_COMMENT +.. title: Blogging with org mode and nikola +.. slug: blogging-with-org-mode-and-nikola +.. date: 2017-01-02 12:00:00 UTC +.. tags: emacs, python +.. category: emacs +.. description: How I use emacs and nikola to write my blog +.. type: text +#+END_COMMENT +#+END_EXAMPLE + +Nikola lets you create your blog entries in various formats like org, markdown, rtf and ascii doc as a few examples, +The main advantage is that you don't need a database and can redistribute your files as plain documents, if you use orgmode format in particular this works very well because you can +export to various formats like pdf or word documents which works great for sending to people or for making offline or printable copies. +Basically you get a lot of power with little effort. + +** Updating nikola configuration +To use org you will need to adjust =conf.py= the examples below are the relevant parts to find and update with the org extensions. +=ORG_REGEX_REPLACE= is only needed if your using my modified plugin. +#+BEGIN_EXAMPLE +POSTS = ( + ("posts/*.rst", "posts", "post.tmpl"), + ("posts/*.txt", "posts", "post.tmpl"), + ("posts/*.org", "posts", "post.tmpl"), + ("posts/*.md", "posts", "post.tmpl"), +) +PAGES = ( + ("stories/*.rst", "stories", "story.tmpl"), + ("stories/*.txt", "stories", "story.tmpl"), + ("stories/*.org", "stories", "story.tmpl"), + ("stories/*.md", "stories", "story.tmpl"), +) +#+END_EXAMPLE + +#+BEGIN_EXAMPLE +COMPILERS = { + "rest": ('.rst', '.txt'), + "markdown": ('.md', '.mdown', '.markdown'), + "textile": ('.textile',), + "txt2tags": ('.t2t',), + "bbcode": ('.bb',), + "wiki": ('.wiki',), + "ipynb": ('.ipynb',), + "html": ('.html', '.htm'), + "orgmode": ('.org',), + "php": ('.php',), +} +#+END_EXAMPLE + +#+BEGIN_EXAMPLE +ORG_REGEX_REPLACE = [(r'src\=\"(../)*', 'src="/')] + +ORG_PRETTY_URL_ADJUST = True +#+END_EXAMPLE + +** Hosting your blog +I hast my blog on my own server and domain which is an option but you can also deploy your blog to github pages if you don't want the cost and hassle of maintaining your own environment. + +** Containers +The setup I use below involves using containers to generate deploy an host the container, this means the setup is portable you just need docker available on your machine the rest of the technologies are installed inside the containers. +If you want a similar setup you can copy the docker compose and drone files below. + +To generate your blog you just need to run the following command. + +#+BEGIN_SRC shell +docker-compose up +#+END_SRC + +** Nikola org file tips +*** Hiding posts +If you want to deploy but hide your posts from the index's you can tag them with draft. +#+BEGIN_EXAMPLE +.. tags: emacs, python, draft +#+END_EXAMPLE +You can also stop your posts from being deploy altogether by putting =DEPLOY_DRAFT = False= in your nikola config. +see https://getnikola.com/handbook.html#drafts for more information. + +*** Inline images +Emacs lets you render your images direct in your document which is great for verifying your image paths, however there is an issue with nikola currently which means it changes the depth of your +post so the deployed images will not display: + +You have a couple of options ++ Manually put in the correct path for nikola and loose the display and verification that the path is correct, you can still run the server locally to verify. ++ The other option is to use a patched version which I am doing, this means i never actually need to test my pages I can write the post commit and be confident its correct. +https://github.com/olymk2/plugins ++ Bug the dev's they are not planning on fixing this currently but if enough people want it fixed they might, so if you want to get this fixed i suggest you add to the issue below or raise a new issue. +https://github.com/getnikola/plugins/issues/188 + +** Docker images for rendering your org files +You can use the container below to generate your blog, its a simple container with emacs added with org mode which is called to generate the html pages. + +#+BEGIN_SRC yaml +FROM olymk2/emacs + +RUN apt-get update +RUN apt-get install -y locales locales-all python3 python3-pip python3-gdbm + +RUN dpkg-reconfigure locales + +ENV LC_ALL C.UTF-8 + +RUN locale-gen en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANG C.UTF-8 +ENV LANGUAGE en_US:en + +COPY ./build/requirements.txt /app/requirements.txt + +RUN pip3 install -r /app/requirements.txt +RUN nikola plugin -i orgmode + +WORKDIR /app +ENTRYPOINT nikola clean; nikola build +#+END_SRC + +** Auto deploy with drone +I use drone to deploy my blog you can use other CI services, but using drone keeps it simple as i can use the same containers I host my site with. + +#+BEGIN_SRC yaml +workspace: + base: /drone + path: src + +pipeline: + publish: + pull: True + image: appleboy/drone-ssh + host: ${SSH_DOMAIN} + user: ${SSH_USER} + key: ${SSH_KEY} + port: 22 + command_timeout: 240 + script: + - cd /var/www/your-site-location/ + - git pull + - docker-compose build + - docker-compose up --remove-orphans + - echo 'Finished deploy' +#+END_SRC +** Nikola Emacs plugin +I have been working on a simple plugin for emacs to give you a magit style menu to nikola from inside nikola if you would like to try this visit the link below. +https://github.com/olymk2/emacs-nikola diff --git a/bases/do-blog/resources/documents/emacs/common-org-header-arguments.org b/bases/do-blog/resources/documents/emacs/common-org-header-arguments.org new file mode 100755 index 0000000..b278cf8 --- /dev/null +++ b/bases/do-blog/resources/documents/emacs/common-org-header-arguments.org @@ -0,0 +1,32 @@ +#+TITLE: org mode header arguments reference +#+DATE: 2017-07-01 12:00:00 UTC +#+DESCRIPTION: common headers for modifying the export output +#+FILETAGS: emacs:orgmode +#+CATEGORY: emacs +#+SLUG: org-mode-header-reference +#+BEGIN_COMMENT +.. title: org mode header arguments reference +.. slug:org-mode-header-reference +.. date: 2017-07-01 12:00:00 UTC +.. tags: emacs, orgmode +.. category: emacs +.. description: common headers for modifying the export output +.. type: text +#+END_COMMENT + + + +** Disable table of contents +#+BEGIN_EXAMPLE +#+OPTIONS: toc:nil +#+END_EXAMPLE + +** Preserve line breaks +#+BEGIN_EXAMPLE +#+OPTIONS: \n:t +#+END_EXAMPLE + +** Disable header numbers +#+BEGIN_EXAMPLE +#+OPTIONS: num:nil +#+END_EXAMPLE diff --git a/bases/do-blog/resources/documents/emacs/emacs-widget-examples.org b/bases/do-blog/resources/documents/emacs/emacs-widget-examples.org new file mode 100755 index 0000000..f71778e --- /dev/null +++ b/bases/do-blog/resources/documents/emacs/emacs-widget-examples.org @@ -0,0 +1,229 @@ +#+TITLE: Capturing user input with the widget library +#+DATE: 2018-05-23 12:00:00 UTC +#+DESCRIPTION: Exploration into capturing user data using emacs widget library +#+FILETAGS: emacs:widgets:draft +#+CATEGORY: emacs +#+SLUG: emacs-user-input-via-widgets +#+BEGIN_COMMENT +.. title: Capturing user input with the widget library +.. slug: emacs-user-input-via-widgets +.. date: 2018-05-23 12:00:00 UTC +.. tags: emacs, widgets, draft +.. category: emacs +.. description: Exploration into capturing user data using emacs widget library +.. type: text +#+END_COMMENT + +After writting a emacs plugin I had need to take a lot of optional user input. +Seems the options are emacs widgets which is built in or using something like magit popups +which allows the user to selectively enter values. + +I will focus on emacs widgets because some of the input could be quite large. + +** Getting started +To get started creating your blog first install nikola with pip or use the docker container, then run =nikola init= to create your new blog. + +#+BEGIN_SRC emacs-lisp :results silent + (require 'widget) + + (eval-when-compile + (require 'wid-edit)) + + (defun widget-example-01 () + "Create the widgets from the Widget manual." + (interactive) + ;; create a new buffer and wrap all commands in buffer context + (with-current-buffer (switch-to-buffer "Widget Test 1") + + ;; Reset back to defaults + (kill-all-local-variables) + (make-local-variable 'widget-example-repeat) + + ;; Clean up buffers + (let ((inhibit-read-only t)) + (erase-buffer)) + + ;; Insert description text into buffer + (widget-insert "Fill in the form below\n\n") + + ;; Create our first widget with a text label + (widget-insert "Server Name: ") + (widget-create 'editable-field + :size 13 + "Replace Me") + (widget-insert "\n----\n") + + + ;; Modify the current buffer to work with widgets + (widget-setup))) +#+END_SRC + +#+RESULTS: +: widget-example-01 + +** Adding submit and reset buttons + +#+BEGIN_SRC emacs-lisp + (require 'widget) + + (eval-when-compile + (require 'wid-edit)) + + (defvar widget-example-repeat) + + (defun widget-example-02 () + "Create the widgets from the Widget manual." + (interactive) + (with-current-buffer (switch-to-buffer "Widget Test 2") + (kill-all-local-variables) + (make-local-variable 'widget-example-repeat) + (let ((inhibit-read-only t)) + (erase-buffer)) + (widget-insert "Fill in the form below\n\n") + (widget-insert "Server Name: ") + (widget-create 'editable-field + :size 13 + "Replace Me") + (widget-insert "\n----\n") + + (widget-create 'push-button + :notify (lambda (&rest ignore) + (if (= (length (widget-value widget-example-repeat)) + 3) + (message "Congratulation!") + (error "Three was the count!"))) + "Apply Form") + (widget-insert " ") + (widget-create 'push-button + :notify (lambda (&rest ignore) + (widget-example)) + "Reset Form") + (widget-insert "\n----\n") + + (widget-setup))) +#+END_SRC + +#+RESULTS: +: widget-example-02 + +#+BEGIN_SRC emacs-lisp + (require 'widget) + + (eval-when-compile + (require 'wid-edit)) + + (defvar widget-example-repeat) + + + (defun widget-example () + "Create the widgets from the Widget manual." + (interactive) + (switch-to-buffer "*Digitalocean*") + (kill-all-local-variables) + (make-local-variable 'widget-example-repeat) + (let ((inhibit-read-only t)) + (erase-buffer)) + (widget-insert "Digitalocean droplet creation") + (widget-insert "Name: ") + (widget-create 'editable-field + :size 13 + "My Name") + (widget-insert "Region: ") + (widget-create 'editable-field + :size 13 + "Your Region") + (widget-insert "Size: ") + (widget-create 'editable-field + :size 13 + "Your Size") + (widget-insert "Image: ") + (widget-create 'editable-field + :size 13 + "Your Image") + + (widget-insert "Image: ") + (widget-create 'editable-field + :size 13 + "Your Image") + (widget-insert "Image: ") + (widget-create 'editable-field + :size 13 + "Your Image") + (widget-insert "Image: ") + (widget-create 'editable-field + :size 13 + "Your Image") + (widget-insert "Image: ") + (widget-create 'editable-field + :size 13 + "Your Image") + (widget-insert "Image: ") + (widget-create 'editable-field + :size 13 + "Your Image") + + (widget-create 'menu-choice + :tag "Choose" + :value "This" + :help-echo "Choose me, please!" + :notify (lambda (widget &rest ignore) + (message "%s is a good choice!" + (widget-value widget))) + '(item :tag "This option" :value "This") + '(choice-item "That option") + '(editable-field :menu-tag "No option" "Thus option")) + (widget-insert "Address: ") + (widget-create 'editable-field + "Some Place\nIn some City\nSome country.") + (widget-insert "\nSee also ") + (widget-create 'link + :notify (lambda (&rest ignore) + (widget-value-set widget-example-repeat + '("En" "To" "Tre")) + (widget-setup)) + "other work") + (widget-insert " for more information.\n\nNumbers: count to three below\n") + (setq widget-example-repeat + (widget-create 'editable-list + :entry-format "%i %d %v" + :notify (lambda (widget &rest ignore) + (let ((old (widget-get widget + ':example-length)) + (new (length (widget-value widget)))) + (unless (eq old new) + (widget-put widget ':example-length new) + (message "You can count to %d." new)))) + :value '("One" "Eh, two?" "Five!") + '(editable-field :value "three"))) + (widget-insert "\n\nSelect multiple:\n\n") + (widget-create 'checkbox t) + (widget-insert " This\n") + (widget-create 'checkbox nil) + (widget-insert " That\n") + (widget-create 'checkbox + :notify (lambda (&rest ignore) (message "Tickle")) + t) + (widget-insert " Thus\n\nSelect one:\n\n") + (widget-create 'radio-button-choice + :value "One" + :notify (lambda (widget &rest ignore) + (message "You selected %s" + (widget-value widget))) + '(item "One") '(item "Another One.") '(item "A Final One.")) + (widget-insert "\n") + (widget-create 'push-button + :notify (lambda (&rest ignore) + (if (= (length (widget-value widget-example-repeat)) + 3) + (message "Congratulation!") + (error "Three was the count!"))) + "Apply Form") + (widget-insert " ") + (widget-create 'push-button + :notify (lambda (&rest ignore) + (widget-example)) + "Reset Form") + (widget-insert "\n") + (use-local-map widget-keymap) + (widget-setup)) +#+END_SRC diff --git a/bases/do-blog/resources/documents/emacs/merge-staged-sql-files-into-update-rollback-scripts.org b/bases/do-blog/resources/documents/emacs/merge-staged-sql-files-into-update-rollback-scripts.org new file mode 100755 index 0000000..44e63f0 --- /dev/null +++ b/bases/do-blog/resources/documents/emacs/merge-staged-sql-files-into-update-rollback-scripts.org @@ -0,0 +1,53 @@ +#+TITLE: Auto generated update and rollback database scripts with magit and emacs +#+DATE: 2017-07-25 12:00:00 UTC +#+DESCRIPTION: Useful functions for creating database update and rollback scripts +#+FILETAGS: emacs:sql:git +#+CATEGORY: emacs +#+SLUG: auto-generating-update-and-rollback-scripts-using-magit +#+BEGIN_COMMENT +.. title: Auto generated update and rollback database scripts with magit and emacs +.. slug: auto-generating-update-and-rollback-scripts-using-magit +.. date: 2017-07-25 12:00:00 UTC +.. tags: emacs, sql, git +.. category: emacs +.. description: Useful functions for creating database update and rollback scripts +.. type: text +#+END_COMMENT + +This code loops over all .sql .pks and .pkb files and merges them into the current buffer, it currently need to be run from the projects root. + +Magit has some useful methods we can utilise to get a list of staged files, from there we can join the results into the current buffer. +We can also use magit functions to get the same list before modification to generate a rollback. + +Get previous version of staged files and generate a rollback script, we can use =magit-find-file-noselect= to get an older version. +#+BEGIN_SRC emacs-lisp +(defun generate-oracle-rollback-script () + (interactive) + (insert "SET DEFINE OFF;\n\n") + (loop for i in (cddr (magit-staged-files)) + if (string-match "\\(\.sql\\|\.pkb\\|\.pks\\)" i) + collect (insert (concat "-- START AUTO GENERATED " i "\n" (with-current-buffer + (magit-find-file-noselect "HEAD" + (expand-file-name i)) (buffer-string)) + ) + "\n-- END AUTO GENERATED " i "\n\n")) + ) +#+END_SRC + +Get all staged files and generate an update script, we can get a list of staged files with =magit-staged-files= and insert into the current buffer with =insert-file-contents=. +#+BEGIN_SRC emacs-lisp +(defun generate-oracle-update-script () + (interactive) + (insert "SET DEFINE OFF;\n\n") + (loop for i in (cddr (magit-staged-files)) + if (string-match "\\(\.sql\\|\.pkb\\|\.pks\\)" i) + collect (insert (concat "-- START AUTO GENERATED " i "\n" (with-temp-buffer + (insert-file-contents + (expand-file-name i)) + (buffer-string)) + "\n-- END AUTO GENERATED " i "\n\n")) + ) +) +#+END_SRC + +Future enhancement would be to detect the project root from any buffer with in the project you have open. diff --git a/bases/do-blog/resources/documents/emacs/using-sql-with-org-mode.org b/bases/do-blog/resources/documents/emacs/using-sql-with-org-mode.org new file mode 100755 index 0000000..e303b2c --- /dev/null +++ b/bases/do-blog/resources/documents/emacs/using-sql-with-org-mode.org @@ -0,0 +1,54 @@ +#+TITLE: Running sql in org mode +#+DATE: 2017-06-20 12:00:00 UTC +#+DESCRIPTION: Example on how to run sql statements and display the results +#+FILETAGS: emacs:sql:draft +#+CATEGORY: emacs +#+SLUG: running-sql-with-org-mode +#+BEGIN_COMMENT +.. title: Running sql in org mode +.. slug: running-sql-with-org-mode +.. date: 2017-06-20 12:00:00 UTC +.. tags: emacs, sql, draft +.. category: emacs +.. description: Example on how to run sql statements and display the results +.. type: text +#+END_COMMENT + +When searching for this i found the information was incomplete or just plain did not work, after some trial and error and working things out i got it working and have created the examples for future reference. + +** Specifying your connection in the document header +This makes the connection available to all source code blocks by default +*** Oracle example +#+BEGIN_EXAMPLE +#+PROPERTY: header-args :engine oracle :dbhost 172.17.0.2 :dbport 1521 :dbuser mydb_username :dbpassword mydb_password :database XE +#+END_EXAMPLE + +Specifying your connection in a block +#+BEGIN_EXAMPLE +:PROPERTIES: +header-args: engine oracle +header-args: dbhost 172.17.0.2 +header-args: dbport 1521 +header-args: dbuser mydb_username +header-args: dbpassword mydb_password +header-args: database XE +:END: +#+END_EXAMPLE + +** Specifying your connection as part of your source block +You must not have any blank lines between the connection header pram's and the starting source code block else it will not work. +#+BEGIN_EXAMPLE +#+name: my-query +#+header: :engine oracle +#+header: :dbhost 172.17.0.2 +#+header: :dbport 1521 +#+header: :dbuser mydb_username +#+header: :dbpassword mydb_password +#+header: :database XE +#+begin_src sql +select * from dual; +#+end_src +#+END_EXAMPLE + + + diff --git a/bases/do-blog/resources/documents/hardware/gaups-test.org b/bases/do-blog/resources/documents/hardware/gaups-test.org new file mode 100644 index 0000000..1cffa85 --- /dev/null +++ b/bases/do-blog/resources/documents/hardware/gaups-test.org @@ -0,0 +1,206 @@ +#+TITLE: GRBL CNC gcode test code +#+DATE: 2018-10-12 12:00:00 UTC +#+DESCRIPTION: CNC cutting test code +#+FILETAGS: cnc:gcode +#+CATEGORY: cnc, tech +#+SLUG: grbl-cnc-test-gcode + +* Usefull codes used in the generated gcode. +Set millimeters with G21 Start spindle clockwise with M3 setting the speed. +Set the start position with G92 specifying the cordinates, move to start position with G0 rapid or G1 for cutting speed. + +To draw a circle we need to position the cutting bit on the circumference of the circle, we then set the I and J positions which are the distance from the center of the circle. + +I is the distance from arc start-point to arc-center-point in the X-axis. +J is the distance from arc start-point to arc-center-point in the Y-axis + +* Job setup +Initially job setup using in millimeters, start the spindle no speed because wee manually set the speed. +Set the current position then move to the first cutting position. +#+BEGIN_SRC conf :tangle circle-test.gcode +G21 +M3 S0.0000 +G92 X0 Y0 Z5 +G0 X5.0 Y5.0 Z5.0000 +#+END_SRC + +* Cut first circle 0.1mm at a time +#+BEGIN_SRC conf :tangle circle-test.gcode + ;;Draw circle cutting down 0.1mm at a time. + G1 X0.0 Y0.0 Z5.0000 + + G1 X5.0 Y5.0 Z3.0000 + G2 X5.0 Y5.0 Z3.0000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.9000 + G2 X5.0 Y5.0 Z2.9000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.8000 + G2 X5.0 Y5.0 Z2.8000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.7000 + G2 X5.0 Y5.0 Z2.7000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.6000 + G2 X5.0 Y5.0 Z2.6000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.5000 + G2 X5.0 Y5.0 Z2.5000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.4000 + G2 X5.0 Y5.0 Z2.4000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.3000 + G2 X5.0 Y5.0 Z2.3000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.2000 + G2 X5.0 Y5.0 Z2.2000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.1000 + G2 X5.0 Y5.0 Z2.1000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z2.0000 + G2 X5.0 Y5.0 Z2.0000 I2.5 J2.5 F12000.00 + + G1 X5.0 Y5.0 Z1.9000 + G2 X5.0 Y5.0 Z1.9000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.8000 + G2 X5.0 Y5.0 Z1.8000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.7000 + G2 X5.0 Y5.0 Z1.7000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.6000 + G2 X5.0 Y5.0 Z1.6000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.5000 + G2 X5.0 Y5.0 Z1.5000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.4000 + G2 X5.0 Y5.0 Z1.4000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.3000 + G2 X5.0 Y5.0 Z1.3000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.2000 + G2 X5.0 Y5.0 Z1.2000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.1000 + G2 X5.0 Y5.0 Z1.1000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z1.0000 + G2 X5.0 Y5.0 Z1.0000 I2.5 J2.5 F12000.00 + + G1 X5.0 Y5.0 Z0.9000 + G2 X5.0 Y5.0 Z0.9000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.8000 + G2 X5.0 Y5.0 Z0.8000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.7000 + G2 X5.0 Y5.0 Z0.7000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.6000 + G2 X5.0 Y5.0 Z0.6000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.5000 + G2 X5.0 Y5.0 Z0.5000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.4000 + G2 X5.0 Y5.0 Z0.4000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.3000 + G2 X5.0 Y5.0 Z0.3000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.2000 + G2 X5.0 Y5.0 Z0.2000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.1000 + G2 X5.0 Y5.0 Z0.1000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z0.0000 + G2 X5.0 Y5.0 Z0.0000 I2.5 J2.5 F12000.00 + G1 X5.0 Y5.0 Z5.0000 +#+END_SRC + +* Cut second circle 0.2mm at a time +#+BEGIN_SRC conf :tangle circle-test.gcode + ;;Draw circle cutting down 0.2mm at a time. + G1 X0.0 Y0.0 Z5.0000 + + G1 X-5.0 Y5.0 Z3.0000 + G2 X-5.0 Y5.0 Z3.0000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z2.8000 + G2 X-5.0 Y5.0 Z2.8000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z2.6000 + G2 X-5.0 Y5.0 Z2.6000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z2.4000 + G2 X-5.0 Y5.0 Z2.4000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z2.2000 + G2 X-5.0 Y5.0 Z2.2000 I2.5 J2.5 F12000.00 + + G1 X-5.0 Y5.0 Z2.0000 + G2 X-5.0 Y5.0 Z2.0000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z1.8000 + G2 X-5.0 Y5.0 Z1.8000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z1.6000 + G2 X-5.0 Y5.0 Z1.6000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z1.4000 + G2 X-5.0 Y5.0 Z1.4000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z1.2000 + G2 X-5.0 Y5.0 Z1.2000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z1.0000 + G2 X-5.0 Y5.0 Z1.0000 I2.5 J2.5 F12000.00 + + G1 X-5.0 Y5.0 Z0.8000 + G2 X-5.0 Y5.0 Z0.8000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z0.6000 + G2 X-5.0 Y5.0 Z0.6000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z0.4000 + G2 X-5.0 Y5.0 Z0.4000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z0.2000 + G2 X-5.0 Y5.0 Z0.2000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z0.0000 + G2 X-5.0 Y5.0 Z0.0000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y5.0 Z5.0000 +#+END_SRC + +* Cut third circle 0.3mm at a time +#+BEGIN_SRC conf :tangle circle-test.gcode + ;;Draw circle cutting down 0.3mm at a time. + G1 X0.0 Y0.0 Z5.0000 + + G1 X-5.0 Y-5.0 Z3.0000 + G2 X-5.0 Y-5.0 Z3.0000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z2.7000 + G2 X-5.0 Y-5.0 Z2.7000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z2.4000 + G2 X-5.0 Y-5.0 Z2.4000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z2.1000 + G2 X-5.0 Y-5.0 Z2.1000 I2.5 J2.5 F12000.00 + + G1 X-5.0 Y-5.0 Z2.0000 + G2 X-5.0 Y-5.0 Z2.0000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z1.7000 + G2 X-5.0 Y-5.0 Z1.7000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z1.4000 + G2 X-5.0 Y-5.0 Z1.4000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z1.1000 + G2 X-5.0 Y-5.0 Z1.1000 I2.5 J2.5 F12000.00 + + G1 X-5.0 Y-5.0 Z1.0000 + G2 X-5.0 Y-5.0 Z1.0000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z0.7000 + G2 X-5.0 Y-5.0 Z0.7000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z0.4000 + G2 X-5.0 Y-5.0 Z0.4000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z0.1000 + G2 X-5.0 Y-5.0 Z0.1000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z0.0000 + G2 X-5.0 Y-5.0 Z0.0000 I2.5 J2.5 F12000.00 + G1 X-5.0 Y-5.0 Z5.0000 +#+END_SRC + +* Cut fourth circle 0.4mm at a time +#+BEGIN_SRC conf :tangle circle-test.gcode + ;;Draw circle cutting down 0.4mm at a time. + G1 X0.0 Y0.0 Z5.0000 + + G1 X5.0 Y-5.0 Z3.0000 + G2 X5.0 Y-5.0 Z3.0000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z2.6000 + G2 X5.0 Y-5.0 Z2.6000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z2.2000 + G2 X5.0 Y-5.0 Z2.2000 I2.5 J2.5 F12000.00 + + G1 X5.0 Y-5.0 Z2.0000 + G2 X5.0 Y-5.0 Z2.0000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z1.6000 + G2 X5.0 Y-5.0 Z1.6000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z1.2000 + G2 X5.0 Y-5.0 Z1.2000 I2.5 J2.5 F12000.00 + + G1 X5.0 Y-5.0 Z1.0000 + G2 X5.0 Y-5.0 Z1.0000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z0.6000 + G2 X5.0 Y-5.0 Z0.6000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z0.2000 + G2 X5.0 Y-5.0 Z0.2000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z0.0000 + G2 X5.0 Y-5.0 Z0.0000 I2.5 J2.5 F12000.00 + G1 X5.0 Y-5.0 Z5.0000 +#+END_SRC + diff --git a/bases/do-blog/resources/documents/hardware/gaups.org b/bases/do-blog/resources/documents/hardware/gaups.org new file mode 100755 index 0000000..1585633 --- /dev/null +++ b/bases/do-blog/resources/documents/hardware/gaups.org @@ -0,0 +1,57 @@ +#+TITLE: GAUPS usage and setup +#+DATE: 2016-12-04 12:00:00 UTC +#+DESCRIPTION: Useful notes for setting up and configuring GAUPS +#+FILETAGS: cnc:arduino:serial +#+CATEGORY: cnc, tech +#+SLUG: gaups-usages-and-setup +#+BEGIN_COMMENT +.. title: GAUPS usage and setup +.. slug: gaups-usages-and-setup +.. date: 2016-12-04 12:00:00 UTC +.. tags: cnc, arduino, serial +.. category: cnc, tech +.. description: Useful notes for setting up and configuring GAUPS +.. type: text +#+END_COMMENT + + +To quite screen hold down CTRL + d then press SHIFT + K and answer Y to exit the screen. +Alternatively CTRL + a then type :quit. +#+BEGIN_SRC sh + screen /dev/ttyACM0 115200 +#+END_SRC + +Running =$$= will list your current config, my current setup is in the example below. +#+BEGIN_EXAMPLE +$0=10 (step pulse, usec) +$1=25 (step idle delay, msec) +$2=0 (step port invert mask:00000000) +$3=0 (dir port invert mask:00000000) +$4=0 (step enable invert, bool) +$5=0 (limit pins invert, bool) +$6=0 (probe pin invert, bool) +$10=19 (status report mask:00010011) +$11=0.010 (junction deviation, mm) +$12=0.002 (arc tolerance, mm) +$13=0 (report inches, bool) +$20=0 (soft limits, bool) +$21=0 (hard limits, bool) +$22=1 (homing cycle, bool) +$23=0 (homing dir invert mask:00000000) +$24=25.000 (homing feed, mm/min) +$25=500.000 (homing seek, mm/min) +$26=250 (homing debounce, msec) +$27=1.000 (homing pull-off, mm) +$100=80.000 (x, step/mm) +$101=80.000 (y, step/mm) +$102=500.000 (z, step/mm) +$110=500.000 (x max rate, mm/min) +$111=500.000 (y max rate, mm/min) +$112=500.000 (z max rate, mm/min) +$120=10.000 (x accel, mm/sec^2) +$121=10.000 (y accel, mm/sec^2) +$122=10.000 (z accel, mm/sec^2) +$130=200.000 (x max travel, mm) +$131=200.000 (y max travel, mm) +$132=200.000 (z max travel, mm) +#+END_EXAMPLE diff --git a/bases/do-blog/resources/documents/hardware/lathe-restoration-part-1.org b/bases/do-blog/resources/documents/hardware/lathe-restoration-part-1.org new file mode 100755 index 0000000..afb2468 --- /dev/null +++ b/bases/do-blog/resources/documents/hardware/lathe-restoration-part-1.org @@ -0,0 +1,58 @@ +#+TITLE: Restoring an old myford ml8 lathe +#+DATE: 2017-09-17 12:00:00 UTC +#+DESCRIPTION: Restoration of myford lathe part 1 +#+FILETAGS: hardware +#+CATEGORY: hardware +#+SLUG: restoring-an-old-myford-ml8-lathe +#+THUMBNAIL: ../../images/hardware/lathe/lathe-01.jpg +#+BEGIN_COMMENT +.. title: Restoring an old myford ml8 lathe +.. slug: restoring-an-old-myford-ml8-lathe +.. date: 2017-09-17 12:00:00 UTC +.. tags: hardware, python +.. category: hardware +.. description: Restoration of myford lathe part 1 +.. type: text +#+END_COMMENT + + +Recently i was given an old Myford ML8 lathe which was working but very rusty, i have never really tried dismantling and rebuild this type of machine before so this will be a learning experience. + + +#+CAPTION: lathe original state 01 +[[../../images/hardware/lathe/lathe-01.jpg]] + +#+CAPTION: lathe original state 02 +[[../../images/hardware/lathe/lathe-02.jpg]] + +#+CAPTION: lathe original state 03 +[[../../images/hardware/lathe/lathe-03.jpg]] + + +Been cleaning the parts removing the rust and paint with a wire brush drill attachement, and smaller versions on a dremel for the awkward bit. + +The smaller metal parts i have soaked in vinegar then rubed down with wire wool and sprayed with oil to top them re rusting. + +#+CAPTION: lathe cleaning 01 +[[../../images/hardware/lathe/lathe-04.jpg]] + +#+CAPTION: lathe cleaning 02 +[[../../images/hardware/lathe/lathe-05.jpg]] + +#+CAPTION: lathe cleaning 03 +[[../../images/hardware/lathe/lathe-06.jpg]] + +After cleaning up the parts, i have primed them in some metal paint to help protect from rust, +currently jut primed but i like the colour so will likely keep it bright red. + +#+CAPTION: lathe cleaning 01 +[[../../images/hardware/lathe/lathe-07.jpg]] + +#+CAPTION: lathe cleaning 02 +[[../../images/hardware/lathe/lathe-08.jpg]] + +#+CAPTION: lathe cleaning 03 +[[../../images/hardware/lathe/lathe-09.jpg]] + +Sill lots of cleaning an painting todo, got a stuck pulley which will not come free from the shaft, and need to strip back the main housing and clean. + diff --git a/bases/do-blog/resources/documents/hardware/nfc-raspberry-pi.org b/bases/do-blog/resources/documents/hardware/nfc-raspberry-pi.org new file mode 100644 index 0000000..21b7fd4 --- /dev/null +++ b/bases/do-blog/resources/documents/hardware/nfc-raspberry-pi.org @@ -0,0 +1,106 @@ +#+TITLE: NFC MODULE V3 Raspberry pi +#+DATE: 2018-01-07 12:00:00 UTC +#+DESCRIPTION: How to wire and install software to drive a PN532 +#+FILETAGS: tech:pi:nfc +#+CATEGORY: hardware +#+SLUG: nfc-module-v3-raspberry-pi +#+THUMBNAIL: ../../images/hardware/PN532-PI.png +#+BEGIN_COMMENT +.. title: NFC MODULE V3 Raspberry pi +.. slug: nfc-module-v3-raspberry-pi +.. date: 2018-01-07 12:00:00 UTC +.. tags: pi, nfc +.. category: tech, pi, nfc +.. description: How to wire and install software to drive a PN532 +.. type: text +#+END_COMMENT + +The below guide is to setup a pn532 nfc reader on a raspberry pi. + +First up make surer the PN532 is setup in UART mode, and connect to the pi in the same manor as the diagram below. + +#+CAPTION: PN532 Rfid read connected to a Raspberry PI +[[../../images/hardware/PN532-PI.png]] + + +Before getting started setup your pi with the latest debian image and complete your standard setup. +Once the pi is setup run the commands below installing the necessary libraries. + +Run the below commands to install the tools and libraries we will need for the rest of the setup. +#+BEGIN_SRC sh + sudo apt-get install git build-essential autoconf libtool libpcsclite-dev + sudo apt-get install libusb-dev libcurl4-openssl-dev libjson-c-dev python-pip +#+END_SRC + +Download the nfc library from github. +#+BEGIN_SRC sh +git clone https://github.com/nfc-tools/libnfc.git +cd libnfc +sudo mkdir -p /etc/nfc/devices.d/ +#+END_SRC + + +Copy the relevant config to the correct place on the pi. +#+BEGIN_SRC sh +sudo cp contrib/libnfc/pn532_uart_on_rpi_3.conf.sample /etc/nfc/devices.d/pn532_uart_on_rpi_3.conf +#+END_SRC + +Compile the libnfc library. +#+BEGIN_SRC sh +autoreconf -vis +./configure --with-drivers=pn532_uart --sysconfdir=/etc --prefix=/usr +sudo make clean && sudo make install all +#+END_SRC + + +Install the python library +#+BEGIN_SRC sh +pip install -U nfcpy +#+END_SRC + + +To check everything is working try running the command below to make sure your reader is detected and listed. +#+BEGIN_SRC sh +nfc-scan-device +#+END_SRC + +Now create your first python program to make sure everything is working. +#+BEGIN_SRC python +import nfc +import ndef +from nfc.tag import tt1 +from nfc.tag import tt2 +from nfc.tag import tt3 +from nfc.tag import tt4 + + +tagtypes = ( + ('uid', nfc.tag.tt1.Type1Tag), + ('uid', nfc.tag.tt2.Type2Tag), + ('idm', nfc.tag.tt3.Type3Tag), + ('uid', nfc.tag.tt4.Type4Tag) +) + +def connected(tag): + print tag.type + for uid, type in tgtypes: + if isinstance(tag, type): + print str(attr(tag, uid)).encode("hex") + return + print "error: unknown tag type" + +with nfc.ContactlessFrontend('tty:S0:pn532') as clf: + print('waiting for connection') + print(clf) + tag = clf.connect(rdwr={'on-connect': lambda tag: False}) + print(str(tag.identifier).encode('hex')) + print(tag.type) + if not tag.ndef: + print('no ndef data') + else: + for record in tag.ndef.records: + print(record) +#+END_SRC + + + diff --git a/bases/do-blog/resources/documents/hardware/sumo-ring.org b/bases/do-blog/resources/documents/hardware/sumo-ring.org new file mode 100755 index 0000000..b011548 --- /dev/null +++ b/bases/do-blog/resources/documents/hardware/sumo-ring.org @@ -0,0 +1,59 @@ +#+TITLE: Building a sumo robot ring +#+DATE: 2017-08-21 12:00:00 UTC +#+DESCRIPTION: Attempt at building a largish sumo ring +#+FILETAGS: diy:robots:hackspace +#+CATEGORY: hardware +#+SLUG: building-a-sumo-robot-ring +#+THUMBNAIL: ../../images/hardware/sumo-ring/IMG_20170813_093622.jpg +#+BEGIN_COMMENT +.. title: Building a sumo robot ring +.. slug: building-a-sumo-robot-ring +.. date: 2017-08-21 12:00:00 UTC +.. tags: diy, robots, hackspace +.. category: hardware +.. description: Attempt at building a largish sumo ring +.. type: text +#+END_COMMENT + + +We have decided to build some sumo robot at the hackspace, so i decided to take up this challenge here are some pictures from the build. + + +** Problems +Attempting to cut out a large circle with the tools i had available proved to be tricky, tried using a router attached to some ply initally but this struggled to cut well. + +In the end i used a jigsaw this worked quite well but the curve of the circle would move the blade away from its centre guild and caused me to take some chunks out that i had not in tended. + +** The build + +#+CAPTION: Joining two sheets of mdf together +[[../../images/hardware/sumo-ring/IMG_20170624_160836.jpg]] +#+CAPTION: Cutting the circle with a jig bolted to a piece of wood +[[../../images/hardware/sumo-ring/IMG_20170625_105122.jpg]] +#+CAPTION: The inaccuracy +[[../../images/hardware/sumo-ring/IMG_20170625_113520.jpg]] +#+CAPTION: Fixing the inaccuracy +[[../../images/hardware/sumo-ring/IMG_20170729_113420.jpg]] +#+CAPTION: Masking for the lines +[[../../images/hardware/sumo-ring/IMG_20170811_173114.jpg]] + +** The final ring + +I am reasonably pleased with the outcome it certainly could have been better, if i had cut the initial circle out more accurately also the rim could have done with being thicker to support the weight, +this would likely have been harder to bend with the tools I had available. + +#+CAPTION: The final ring +[[../../images/hardware/sumo-ring/IMG_20170813_093622.jpg]] + + +** Lets build some robots +So now we have a ring lets build some robots, we can use them to test ideas for pi wars next year as well :) + +I propose controlled for now, and small in size perhaps with these dimensions based on online dimensions. + +500 millimetres high +200 millimetres width +200 millimetres depth +3000g weight limit + + diff --git a/bases/do-blog/resources/documents/hardware/weather-station.org b/bases/do-blog/resources/documents/hardware/weather-station.org new file mode 100644 index 0000000..38f2656 --- /dev/null +++ b/bases/do-blog/resources/documents/hardware/weather-station.org @@ -0,0 +1,109 @@ +#+TITLE: Weather Station +#+DATE: 2018-10-12 12:00:00 UTC +#+DESCRIPTION: CNC cutting test code +#+FILETAGS: cnc:gcode:draft +#+CATEGORY: cnc, tech, draft +#+SLUG: grbl-cnc-test-gcode + + +Venus hottest planet in the solar system due to co2 + +docker run -it -p 1883:1883 -p 9001:9001 -v mosquitto.conf:/mosquitto/config/mosquitto.conf eclipse-mosquitto + +Amount of oxygen in the atmosphere 20.95% + + 😄, 😊, 😐, 😟, 😩 + +250-350 ppm | normal outdoor air level +350-1,000 ppm | Indoor good air exchange +1,000-2,000 ppm | Complaints of drowsiness and poor air +2,000-5,000 ppm | level associated with headaches, sleepiness, and stagnant, stale, stuffy air; poor concentration, loss of attention, increased heart rate and slight nausea may also be present. +>5,000 ppm | This indicates unusual air conditions where high levels of other gases also could be present. Toxicity or oxygen deprivation could occur. This is the permissible exposure limit for daily workplace exposures. + +| Product Model | MH-Z19B | +| Target Gas | CO2 | +| Working voltage | 4.5~ 5.5 V DC | +| Average current | < 60mA(@5V) | +| Peak current | 150mA (@5V) | +| Interface leve | l3.3 V(Compatiblewith 5V) | +| Measuring range | refer to Table 2 | +| Output signal | UART(TTL 3.3V) PWMDAC(default 0.4-2V) | +| Preheat time | 3 min | +| Response Time | T90< 120 s | +| Working temperature | 0 ~ 50 °C | +| Working humidity | 0~ 90% RH(No condensation) | +| Dimension | 33 mm×20 mm×9 mm(L×W×H) | +| Weight | 5g | +| Lifespan | > 5years | +| | | + +* Duty cycle +The duty cycle is the length of the on in relation to the off percentage of on over off, + +* MQ135 Sensor +https://www.engineeringtoolbox.com/co2-comfort-level-d_1024.html +https://community.particle.io/t/mq135-and-spark-core/12657/23 + +* MHZ19 +https://diyprojects.io/publish-co2-concentration-mh-z19-sensor-domoticz-arduino-code-compatible-esp32-esp8266/#.XG3HIKOnyV4 +https://diyprojects.io/product/deal-self-calibrated-co2-sensor-mh-z19-uart-output-pwm-5000ppm/#.XG3HAaOnyV4 + +Static values used below instead of using magic numbers which mean nothing to some looking over the code. +#+BEGIN_SRC C +// Values from the dataheet +const unsigned long mhz19_ppm_range = 5000; +const unsigned long mhz19_pwm_cycle = 1000; +const unsigned long mhz19_pwm_middle_cycle = 1004; +#+END_SRC + +The below function will read a value from the MHZ19 sensor using pwm and calculate the CO2 +based on the datasheet below. +https://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf +#+BEGIN_SRC C + unsigned long mhz19_sensor() + { + // Wait for the next pulse In + do { + // Work out for how long the pulse was set high + pin_high_time = pulseIn(D6, HIGH, 1004000) / mhz19_pwm_cycle; + } while (pin_high_time == 0); + + // work out how long the pulse was set low, by taking high from total length + pin_low_time = mhz19_pwm_middle_cycle - pin_high_time; + + // Calculate parts per million from formula in spec sheet + // CO2level:ppm=2000×(time_high-2ms)/(time_high+time_low-4ms) + ppm = mhz19_ppm_range * (pin_high_time-2)/(pin_high_time+pin_low_time-4); + + return ppm; + } +#+END_SRC + +* Mosquitto server + +#+BEGIN_SRC config + +#+END_SRC + +#+BEGIN_SRC shell +docker run -d -it -p 1883:1883 -p 9001:9001 -v /var/www/mqtt:/mosqu/config/ eclipse-mosquitto +#+END_SRC + +* SPS30 +** I2C connection +Connect the SDA pin to D2 pin and SCL pin to d1 on the wemos to use I2C mode. +** UART connection +Connect the RX and TX pins on the SPS30 to the RX TX pins on the wemos. + +The following UART settings haveto be used: +Baud Rate: 115’200 bit/s +Data Bits: 8 +Parity: None +Stop Bit: 1 + +* Further information +https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=5&cad=rja&uact=8&ved=2ahUKEwj3i7CtjN_hAhXCMewKHS2iCb0Q0PADMAR6BAgAEAU&url=https%3A%2F%2Fvancouversun.com%2Fnews%2Flocal-news%2Frising-co2-on-declining-nutrition-in-food-is-big-issue-ted-talk-hears&usg=AOvVaw0GijmobCE9pyIRmQTWTLFe + + +** Sensors +https://hackaday.com/2019/04/16/picking-the-right-sensors-for-home-automation/ diff --git a/bases/do-blog/resources/documents/lisp/fetch-jira-summary-from-jira-id-under-cursor-with-emacs.org b/bases/do-blog/resources/documents/lisp/fetch-jira-summary-from-jira-id-under-cursor-with-emacs.org new file mode 100755 index 0000000..a4c996e --- /dev/null +++ b/bases/do-blog/resources/documents/lisp/fetch-jira-summary-from-jira-id-under-cursor-with-emacs.org @@ -0,0 +1,48 @@ +#+TITLE: Fetch jira summary from JIRA-ID under cursor with emacs +#+DATE: 2016-11-10 10:45:13 UTC +#+DESCRIPTION: Example to insert jira comment and link at cursor position in emacs +#+FILETAGS: emacs:lisp +#+CATEGORY: lisp +#+SLUG: fetch-jira-summary-from-jira-id-under-cursor-with-emacs +#+BEGIN_COMMENT +.. title: Fetch jira summary from JIRA-ID under cursor with emacs +.. slug: fetch-jira-summary-from-jira-id-under-cursor-with-emacs +.. date: 2016-11-10 10:45:13 UTC +.. tags: lisp, emacs +.. category: lisp +.. link: +.. description: Example to insert jira comment and link at cursor position in emacs +.. type: text +#+END_COMMENT + +The below snippet will grab the JIRA-ID under your cursor and use it to look up the summary and past it next to the JIRA-ID. + +You need to have org-jira installed because this makes use of org-jira-get-issue-by-id to look up the ticket details. + +#+BEGIN_SRC emacs-lisp +(defun org-jira-get-summary () + "insert summary next to ticket id from jira" + (interactive) + (let ((jira_id (thing-at-point 'symbol))) + (forward-symbol 1) + (insert (format " - %s" + (cdr (assoc 'summary (car (org-jira-get-issue-by-id jira_id)))))))) +#+END_SRC + +This next snippet does the same as the first except it also replaces the ID with a link back to jira + +#+BEGIN_SRC emacs-lisp +(defun org-jira-get-summary-url () + "insert summary next to ticket id from jira with url link" + (interactive) + (let ((jira_id (thing-at-point 'symbol))) + (sp-kill-symbol 1) + (insert (format "[[%s][%s]] - %s" (concatenate 'string jiralib-url "browse/" jira_id) jira_id + (cdr (assoc 'summary (car (org-jira-get-issue-by-id jira_id)))))))) +#+END_SRC + +You will need to also place the code below some where to make the jira methods available, hopefully these will be merged directly into org jira soon. + +#+BEGIN_SRC emacs-lisp +(require 'org-jira) +#+END_SRC diff --git a/bases/do-blog/resources/documents/lisp/run-drone-test-suite-from-emacs.org b/bases/do-blog/resources/documents/lisp/run-drone-test-suite-from-emacs.org new file mode 100755 index 0000000..d220370 --- /dev/null +++ b/bases/do-blog/resources/documents/lisp/run-drone-test-suite-from-emacs.org @@ -0,0 +1,38 @@ +#+TITLE: Run drone test suite from emacs +#+DATE: 2016-11-15 12:00:00 UTC +#+DESCRIPTION: Example lisp code that run drone in a shell and outputs into a buffer the results. +#+FILETAGS: emacs:lisp +#+CATEGORY: lisp +#+SLUG: run-drone-test-suite-from-emacs +#+BEGIN_COMMENT +.. title: Run drone test suite from emacs +.. slug: run-drone-test-suite-from-emacs +.. date: 2016-11-15 12:00:00 UTC +.. tags: lisp, emacs +.. category: lisp +.. link: +.. description: Example lisp code that run drone in a shell and outputs into a buffer the results. +.. type: text +#+END_COMMENT + +The first function =drone-root= simply searches for =.drone.yml= by searching up the file system tree from the current files path. + +The second function =drone-exec= runs =drone exec= and output the results to a new buffer called =*drone:=. + + +#+BEGIN_SRC emacs-lisp +(defun drone-root () + (or (locate-dominating-file default-directory ".drone.yml") + (error "Missing .drone.yml not found in directory tree"))) + +;;;###autoload +(defun drone-exec () + "Run \"drone exec\" where .drone.yml is found." + (interactive) + (let ((default-directory (drone-root))) + (with-current-buffer (get-buffer-create (concat "*drone: " default-directory "*")) + (compilation-start (format "drone exec") + nil + (lambda (_) (buffer-name)))))) +#+END_SRC + diff --git a/bases/do-blog/resources/documents/org-meta-index.edn b/bases/do-blog/resources/documents/org-meta-index.edn new file mode 100644 index 0000000..6a452c1 --- /dev/null +++ b/bases/do-blog/resources/documents/org-meta-index.edn @@ -0,0 +1 @@ +() diff --git a/bases/do-blog/resources/documents/project-hosting-setup.org b/bases/do-blog/resources/documents/project-hosting-setup.org new file mode 100644 index 0000000..941763a --- /dev/null +++ b/bases/do-blog/resources/documents/project-hosting-setup.org @@ -0,0 +1,84 @@ + +#+TITLE: Project hosting setup +#+DATE: 2018-11-24 17:22:41 UTC +#+DESCRIPTION: Simple static site hosting. +#+FILETAGS: draft +#+LATEX_CLASS: article +#+CATEGORY: cato +#+SLUG: project-hosting-setup + + +Quick and simple code hosting and CI on a local rasobery pi or in the cloud I use a cheap digitalocen droplet, if you decide to sign up and do the same please use this link and help me out. https://m.do.co/c/f05b87f9fec9 + + +First create a =docker-compose.yml= file, you will need to specify some config in a separate env file. +This compose file brings up caddy as a http server and postgres as the database to persist your data. + +It then uses drone ci linked with gitea for code hosting and continuous integration. +#+BEGIN_SRC yaml +version: '2' + +volumes: + sockets: + driver: local + postgres_data: + driver: local + postgres_backup: + driver: local + +services: + caddy: + image: caddy + volumes: + - /var/www:/var/www + - /var/www/project-hosting-setup/data/caddy/:/data + - /var/www/project-hosting-setup/Caddyfile:/etc/caddy/Caddyfile + - sockets:/data/sockets + ports: + - "80:80" + - "443:443" + + postgres: + image: postgres:9.6 + mem_limit: 256m + volumes: + - postgres_data:/var/lib/postgresql/data + - postgres_backup:/backups + env_file: .env + + gitea: + image: gitea/gitea:1.18.3 + depends_on: + - postgres + ports: + - "10022:22" + volumes: + - ./data/gitea:/data + restart: always + + drone-server: + image: drone/drone:2.16 + volumes: + - /root/.docker/config.json:/root/.docker/config.json + - /var/run/docker.sock:/var/run/docker.sock + - ./data/dronenew:/var/lib/drone/ + depends_on: + - gitea + env_file: .env +#+END_SRC + +Create a =Caddyfile= which will reverse proxy through to your git and ci containers. + +#+BEGIN_SRC text +ci.mydomain.com:443 { + reverse_proxy * drone-server:80 +} + +git.mydomain.com:443 { + reverse_proxy * gitea:3000 +} +#+END_SRC + +https://caddy.community/t/serving-tens-of-thousands-of-domains-over-https-with-caddy/11179 + +Finally create a =.env= file to store your configuration diff --git a/bases/do-blog/resources/documents/python/cad/00-cad-index.org b/bases/do-blog/resources/documents/python/cad/00-cad-index.org new file mode 100755 index 0000000..827c115 --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/00-cad-index.org @@ -0,0 +1,31 @@ +#+TITLE: Python CAD Tutorial - Developing a cad application Index +#+DATE: 2013-10-10 22:00:00 UTC +#+DESCRIPTION: Links to all the articles +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 10-cad-index +#+BEGIN_COMMENT +.. title: Python CAD Tutorial - Developing a cad application Index +.. slug: 10-cad-index +.. date: 2013-10-10 22:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Links to all the articles +.. type: text +#+END_COMMENT + +Linux has been mising a good 3d CAD program for many years, current ones have poor interfaces are missing features or are just to unstable and slow. To help rectify this situation i thought i would write my own but realising i often loose intrest in projects or hit road blocks i decided to write the software as a series of tutorials. +The Aim of these tutorials is to inspire other people to work on cad software using this as a base or just enhancing this software into a functional form, this could also be the base of 3d printer / CNC or laser cutter machines. We will skip the basics of gtk and not go into the maths in a high level of detail, but will aim to have clean well documented code that will be be easy to modify. +If any graphics designers want to mock up a simple but functional interface that looks good please get in touch, or send some designs my way currently its lacks any real design of the interface. +Here are some other suggestions for applications you could write using this code as a base, circuit design application, ui design tool, uml diagrams. + + + Initial Application + + Draw Points + + Setup Camera + + Mouse Cursor + + Draw Lines + + Draw a plane + + Draw a grid + + Create shape / polygon object + + Create a workspace / scene object + + hook up some gui elements diff --git a/bases/do-blog/resources/documents/python/cad/01-cad-initial-application.org b/bases/do-blog/resources/documents/python/cad/01-cad-initial-application.org new file mode 100755 index 0000000..46467f6 --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/01-cad-initial-application.org @@ -0,0 +1,167 @@ +#+TITLE: Python CAD Tutorial 01 - Initial Application +#+DATE: 2013-10-01 12:00:00 UTC +#+DESCRIPTION: Load a glade file and grab the window and display on the screen, attach the close button so we can close the window. +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 01-cad-initial-application +#+THUMBNAIL: ../../../images/cad/tut01-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 01 - Initial Application +.. slug: 01-cad-initial-application +.. date: 2013-10-01 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Load a glade file and grab the window and display on the screen, attach the close button so we can close the window. +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut01][View/Code Download]] + + +#+CAPTION: Point in 3D space +[[../../../images/cad/tut01-interface.png]] + +I have decided to write a series of tutorials in the hope we will see some good CAD applications appear on linux. In my experience, the current options are very unstable or difficult to use. +By writing this program as a series of tutorials, I hope it will encourage contribution to the project or inspire you to develop your own CAD solution. This will hopefully help anyone who would like to write this style of 3D modelling application. +These tutorials will use python, opengl and gtk3 for development. I will not be focusing on performance in this application, but focus more on readability and a clean layout. + +Tools used in development of this application are Glade Interface Designer and Geany IDE, developed on ubuntu 13.04. Both opengl and gtk are cross platform so these examples may work on other platforms and distributions, but have not been tested on anything other than ubuntu 13.04. +[[http://glade.gnome.org][../../../images/cad/glade-icon.png]] +[[http://glade.gnome.org][../../../images/cad/geany.jpeg]] + +The initial GUI has been designed in Glade and has the following layout: a standard menu and toolbar at the top of the window, then from left to right we have a toolbar like in gimp, in the centre we will display our designs and on the right we will have an object list. +At the bottom we will embed an interactive console and a status bar for future expansion. + +The glade interface can be found in the code repository above to open up and adjust. If anyone wants to design a nicer GUI, I would be interested to see what you come up with. +/cad/glade-interface-01.png', title='Glade Interface' +[[../../../images/cad/glade-icon.png]] + +This initial program will contain just enough code to display the glade interface and display an opengl triangle on the screen to prove everything is setup and working and give us a base to build from. + +To get started you will need to install the libraries below from synaptics or by running the command in terminal. + +We then create a configure method which grabs the window X id and sets the size of the viewport (in this case the width and height of the drawing area widget). We then create a drawing start and finish method to call before and after we do any drawing to the screen. + +The last test method is just a test to make sure every thing is setup and functioning correctly and displays the standard OpenGL coloured triangle in the new window. + +#+BEGIN_SRC sh + sudo apt-get install python-gi python-opengl python-gobject python-xlib +#+END_SRC + +This first class separates the OpenGL and xlib code out from the gtk interface. We could reuse the code in another application. We have to use the c library for some xlib functions because the python implementation is limited - it's only used to grab the current xdisplay for OpenGL rendering. + +For some of this code you will need to look at the OpenGL documentations to understand some of the parameters. In the initialise method we setup the OpenGL display parameters we would like to turn on, for example alpha channels for transparency and disable double buffering. We also create a glx context for our OpenGL scene to be drawn to. + +#+BEGIN_SRC python +class gtkgl: + """ These method do not seem to exist in python x11 library lets exploit the c methods """ + xlib = cdll.LoadLibrary('libX11.so') + xlib.XOpenDisplay.argtypes = [c_char_p] + xlib.XOpenDisplay.restype = POINTER(struct__XDisplay) + xdisplay = xlib.XOpenDisplay("") + display = Xlib.display.Display() + attrs = [] + + xwindow_id = None + width = height = 200 + + def __init__(self): + """ Lets setup are opengl settings and create the context for our window """ + + self.add_attribute(GLX.GLX_RGBA, True) + self.add_attribute(GLX.GLX_RED_SIZE, 1) + self.add_attribute(GLX.GLX_GREEN_SIZE, 1) + self.add_attribute(GLX.GLX_BLUE_SIZE, 1) + self.add_attribute(GLX.GLX_DOUBLEBUFFER, 0) + + xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes()) + configs = GLX.glXChooseFBConfig(self.xdisplay, 0, None, byref(c_int())) + self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True) + + def add_attribute(self, setting, value): + """a simple method to nicely add opengl parameters""" + self.attrs.append(setting) + self.attrs.append(value) + + def get_attributes(self): + """ return our parameters in the expected structure""" + attrs = self.attrs + [0, 0] + return (c_int * len(attrs))(*attrs) + + def configure(self, wid): + """ setup up a opengl viewport for the current window, grab the xwindow id and store for later usage""" + self.xwindow_id = GdkX11.X11Window.get_xid(wid) + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ('configure failed running glXMakeCurrent') + glViewport(0, 0, self.width, self.height) + + def draw_start(self): + """make cairo context current for drawing""" + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ('draw failed running glXMakeCurrent') + + def draw_finish(self): + """swap buffer when we have finished drawing""" + GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id) + + def test(self): + """Test method to draw something so we can make sure opengl is working and we can see something""" + self.draw_start() + + glClearColor(0.0, 0.0, 0.0, 0.0) + glClear(GL_COLOR_BUFFER_BIT) + glBegin(GL_TRIANGLES) + glIndexi(0) + glColor3f(1.0, 0.0, 0.0) + glVertex2i(0, 1) + glIndexi(0) + glColor3f(0.0, 1.0, 0.0) + glVertex2i(-1, -1) + glIndexi(0) + glColor3f(0.0, 0.0, 1.0) + glVertex2i(1, -1) + glEnd() + + self.draw_finish() +#+END_SRC + +This class will deal with the GUI interaction between GTK and OpenGL; we will capture mouse events, menu events and button events here and use them to adjust the OpenGL application. + +The initialisation method will load in the interface from glade and grab the widgets we are interested in, at this stage we are only interested in the window and the drawing area widgets. We want to grab the close and draw events for these widgets and set a few parameters against the drawing widget. + +Once we have the drawing area widget we create two methods: configure and draw. The first is called when setting up the drawing area or if we resize it or similar happens we may need to modify the drawing area size. The second method is called whenever the display needs to be updated; we can call this manually or it will get called if another window covers it up to redraw the OpenGL scene. + + +#+BEGIN_SRC python + class gui: + """cad drawing application.""" + glwrap = gtkgl() + + def __init__(self): + """ Initialise the GTK interface grab the GTK widgets, connect the events we are interested in receiving""" + xml = Gtk.Builder() + xml.add_from_file('interface.glade') + self.window = xml.get_object('window') + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + self.window.set_reallocate_redraws(True) + + self.drawing_area = xml.get_object('drawingarea') + self.drawing_area.set_double_buffered(False) + self.drawing_area.set_size_request(self.glwrap.width, self.glwrap.height) + self.drawing_area.connect('configure_event', self.on_configure_event) + self.drawing_area.connect('draw', self.on_draw) + self.drawing_area.connect('realize', self.on_draw) + + self.window.show_all() + + def on_configure_event(self, widget, event): + self.glwrap.configure(widget.get_window()) + return True + + def on_draw(self, *args): + self.glwrap.test() + + def quit(self, *args): + Gtk.main_quit() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/cad/02-cad-draw-points-in-3d-space.org b/bases/do-blog/resources/documents/python/cad/02-cad-draw-points-in-3d-space.org new file mode 100755 index 0000000..7157fec --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/02-cad-draw-points-in-3d-space.org @@ -0,0 +1,85 @@ +#+TITLE: Python CAD Tutorial 02 - Draw points in 3d Space +#+DATE: 2013-10-02 12:00:00 UTC +#+DESCRIPTION: How to draw points in 3D space +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 02-cad-draw-points-in-3d-space +#+THUMBNAIL: ../../../images/cad/tut02-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 02 - Draw points in 3d Space +.. slug: 02-cad-draw-points-in-3d-space +.. date: 2013-10-02 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: How to draw points in 3D space +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut02][View/Download Code]] + +#+CAPTION: Point in 3D space +[[../../../images/cad/tut02-interface.png]] + + In this tutorial we will create a 3D point class and position and draw it to the screen. + +This will be a base class for storing positions in space, we will also implement code to display the points in this class. For now we will hard code a single point in the camera view. We do not have a working camera class yet, so this will be fixed with a position so we know its working.We will store the x, y and z positions and colour for the point along with the display size. The important methods here are the '__init__' and the 'draw' method, the others are helper methods which we will use later on.The helper methods include: + + + The __eq__ method will be usefull for testing if two points share the same location in space. + + __str__ will format the point into a string, which is usefull for debugging. + + get_position will return a numeric version of the point as a tuple. + + +#+BEGIN_SRC python +class createpoint: + x = y = z = 0.0 + display_color = (0, 0, 1) + + def __init__(self, p, c=(0, 1, 0)): + """ Position in 3d space as a tuple or list, and colour in tuple or list format""" + self.point_size = 5 + self.color = c + self.display_color = c + self.x, self.y, self.z = p + + def get_position(self): + """ Return the cordinates as a tuple""" + return self.x, self.y, self.z + + def glvertex(self): + """ Opengl vertex useful so we can dont have to glbegin and glend for each point""" + glVertex3f(self.x, self.y, self.z) + + def __getitem__(self, index): + """ Get a cordinate handy for use in for loops where we want to calculate things""" + return (self.x, self.y, self.z)[index] + + def __str__(self): + """ Print point cordinates useful for debugging""" + return '(%s, %s, %s)' % (str(self.x), str(self.y), str(self.z)) + + def __eq__(self, point): + """ Equality test so we can test if points occupy same space""" + return self.x == point.x and self.y == point.y and self.z == point.z + + def draw(self, c=(0, 1, 0)): + """ Set the size of the point and render""" + glPointSize(self.point_size) + glBegin(GL_POINTS) + glColor3f(self.color[0], self.color[1], self.color[2]) + glVertex3f(self.x, self.y, self.z) + glEnd() +#+END_SRC + +Update the draw method to test out new code works. + +#+BEGIN_SRC python +def on_draw(self, *args): + """ Test code to make sure we can draw a pixel successfully can play with the parameters here""" + glClearColor(0.0, 0.0, 0.0, 0.0) + glClear(GL_COLOR_BUFFER_BIT) + self.glwrap.draw_start() + test_point = point.createpoint((0,0,0.5)) + test_point.draw() + + self.glwrap.draw_finish() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/cad/03-cad-setup-camera-for-viewing-your-scene.org b/bases/do-blog/resources/documents/python/cad/03-cad-setup-camera-for-viewing-your-scene.org new file mode 100755 index 0000000..ff9a02e --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/03-cad-setup-camera-for-viewing-your-scene.org @@ -0,0 +1,125 @@ +#+TITLE: Python CAD Tutorial 03 - Setup the camera for viewing our scene +#+DATE: 2013-10-03 12:00:00 UTC +#+DESCRIPTION: Setup the camera so we can see our models. +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 03-setup-camera-for-viewing-your-scene +#+THUMBNAIL: ../../../images/cad/tut03-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 03 - Setup the camera for viewing our scene +.. slug: 03-setup-camera-for-viewing-your-scene.org +.. date: 2013-10-03 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Setup the camera so we can see our models. +.. type: text +#+END_COMMENT + + +#+BEGIN_SRC python + +#+END_SRC + +[[https://code.launchpad.net/~oly/fabricad/tut03][View/Download Code]] + +[[../../../images/cad/tut03-interface.png]] + + + +In this tutorial we will deal with setting up the camera to view our workspace. The camera can be positioned and adjusted to change what is visible in the application. +Look at the diagram below for the required parameters. The diagram uses the same name as the variables in the code, so you know what values to adjust to change the scene. Most should be self explanatory, for example 'camera location' and 'where we are looking'. +The near and far planes set the size of the visible area, we can only see objects positioned inside this space. The field of view is the viewing angle and will adjust the size of the far_plane accordingly, also changing how much is visible. +The only other parameters you need are the viewport width and height, which is basically the size of the screen or window. In our case, it's the size of the drawingarea widget. + + +web.images.create(web.template.path_image + '/cad/camera-view.svg', title='Camera View') + +The code for this is mainly oriented around the two functions called 'gluPerspective' and 'gluLookAt'. + +'gluLookAt' is relatively straight forward; it takes 3 x,y,z values: the position of the camera, the point we wish to look at and an up vector. In this case we are using the y axis as the up vector. + +'gluPerspective' is where we set what is visible in the world. We pass in our near and far planes and field of view to set the view frustrum area. We need to call the update method whenever something changes like the cameras position. + +#+BEGIN_SRC python + +#+END_SRC + +web.pre.create(load_partial(os.path.abspath(self.path_absolute + '../examples/tut03/cad/camera.py'), 0)) +web.page.section(web.pre.render()) +web.page.section(web.google_plus.render()) + +#+CAPTION: Setup the camera +[[../../../images/cad/tut03-interface.png]] + + +In this tutorial we will deal with setting up the camera to view our workspace. The camera can be positioned and adjusted to change what is visible in the application. +Look at the diagram below for the required parameters. The diagram uses the same name as the variables in the code, so you know what values to adjust to change the scene. +Most should be self explanatory, for example 'camera location' and 'where we are looking'. +The near and far planes set the size of the visible area, we can only see objects positioned inside this space. +The field of view is the viewing angle and will adjust the size of the far_plane accordingly, also changing how much is visible. +The only other parameters you need are the viewport width and height, which is basically the size of the screen or window. In our case, it's the size of the drawingarea widget. + +[[../../../images/cad/camera-view.svg]] + +The code for this is mainly oriented around the two functions called 'gluPerspective' and 'gluLookAt'. 'gluLookAt' is relatively straight forward; it takes 3 x,y,z values: the position of the camera, the point we wish to look at and an up vector. In this case we are using the y axis as the up vector. 'gluPerspective' is where we set what is visible in the world. We pass in our near and far planes and field of view to set the view frustrum area. We need to call the update method whenever something changes like the cameras position. + +#+BEGIN_SRC python +from OpenGL.GL import * +from OpenGL.GLU import * +from OpenGL import GLX +from OpenGL import GL +from ctypes import * +from point import createpoint + + +class createcamera: + #your visible area in 2d space, cordinates of screen or window view space + viewport = 400, 400 + viewport_aspect = viewport[0] / viewport[1] + + #how much of the world can we see, how wide is our sight + field_of_view = 110 + + #camera parameters position look direction rotation + lookat = createpoint((0.0, 0.0, 100.0)) + location = createpoint((0.0, 0.0, 0.0)) + camera_rotation = 0.0 + + #viewing depth of the camera, also know as the frustrum near and far planes + near_plane = 10.0 + far_plane = 100.0 + + #view frustrum / camera visible area properties, + def __init__(self, width, height): + self.viewport = width, height + self.viewport_aspect = self.viewport[0] / self.viewport[1] + + def draw(self): + pass + + #the window has been resized so recalculate display + #also used in initial setup of screen + def update(self): + """call this when ever the screen is updated, + calculates what's visible and where the camera exists in space. + also set what we are loking at in the world""" + glViewport(0, 0, self.viewport[0], self.viewport[1]) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + + #setup camera parameters field of view, size of camera (ie the view window size) + #set the frustrum parameters + gluPerspective(self.field_of_view, + 1.0 * self.viewport[0] / self.viewport[1], + self.near_plane, self.far_plane) + + #position the camera and look at something + gluLookAt(self.location.x, self.location.y, self.location.z, + self.lookat.x, self.lookat.y, self.lookat.z, + 0.0, 1.0, 0.0) + + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() +#+END_SRC + + diff --git a/bases/do-blog/resources/documents/python/cad/04-cad-mouse-cordinates-in-3D-space.org b/bases/do-blog/resources/documents/python/cad/04-cad-mouse-cordinates-in-3D-space.org new file mode 100755 index 0000000..ba0959b --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/04-cad-mouse-cordinates-in-3D-space.org @@ -0,0 +1,111 @@ +#+TITLE: Python CAD Tutorial 04 - Mouse coordinates in 3D space +#+DATE: 2013-10-04 12:00:00 UTC +#+DESCRIPTION: Setup the camera so we can see our models. +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 04-mouse-coordinates-in-3d-space +#+THUMBNAIL: ../../../images/cad/tut04-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 04 - Mouse coordinates in 3D space +.. slug: 04-mouse-coordinates-in-3d-space.org +.. date: 2013-10-04 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Setup the camera so we can see our models. +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut04][View/Download Code]] + + +#+CAPTION: Mouse coordinates +[[../../../images/cad/tut04-interface.png]] + +We have a camera and something displayed on the screen, now let's start taking input from the mouse. We will create a new mouse handling class which will allow us to store a series of clicks. We can also use the stored positions for previews at a later date.The new class contains a few simple methods: + + + append() to add a new click + + count() to return the number of stored clicks + + get_click_position() returns the last clicked position + + get_points() returns a list of the stored locations, and resets the list + + clear() resets the stored coordinates. + +#+BEGIN_SRC python +class mouse_state: + """ store mouse clicks so we know where to draw and what to create """ + button1 = 0 + button2 = 0 + button3 = 0 + cordinates = [] + x = y = 0 + + def append(self, x, y, button=1): + """ store a new mouse click """ + self.x = x + self.y = y + self.cordinates.append((x, y,)) + + def get_click_position(self): + """ return the position of the last click """ + return self.x, self.y + + def get_points(self): + """ return all stored points we may want to store lots of points when drawing a line for example""" + if len(self.cordinates) != 2: + return None + result = self.cordinates + self.cordinates = [] + return result + + def count(self): + """return number of stored points """ + return len(self.cordinates) + + def clear(self): + self.x = self.y = 0 + self.cordinates = [] +#+END_SRC + +Let's add a new method to our camera class, which will convert mouse clicks in 2 dimensional space into 3 dimensional coordinates so we can position something on the screen.The 'get_click_point' method below takes a tuple containing the x and y coordinates from the mouse. It then converts the y position because in opengl '0' is at the bottom, but drawing area widget has '0' at the top. Finally it uses gluUnProject to convert from 2d space to 3d space. + +#+BEGIN_SRC python + def get_click_point(self, pos): + """ convert 2d click in the viewport to 3d point in space""" + viewport = glGetIntegerv(GL_VIEWPORT) + modelview = glGetDoublev(GL_MODELVIEW_MATRIX) + projection = glGetDoublev(GL_PROJECTION_MATRIX) + + #convert screen ccordinate to opengl cordinates, this means modifying the y axes only + x, y = pos[0], self.viewport[1] - pos[1] + + #use unproject to calculate the point and store the resutl in a point object + return createpoint(gluUnProject(x, y, 0.20, modelview, projection, viewport), (250, 0, 0)) +#+END_SRC + +Let's handle the mouse click events in the drawing area; we will store the 2d x and y coordinates of a mouse click in our new mouse_state_class. Now we adjust the on_draw method to draw a point at the location we clicked on the page, which gives us visual feedback that everything is working correctly. + +#+BEGIN_SRC python + def mouse_click(self, widget, event): + self.mouse.append(event.x, event.y, event.button) + self.test_point = self.camera.get_click_point(self.mouse.get_click_position()) + self.on_draw() + + def on_draw(self, *args): + """ Test code to make sure we can draw a pixel successfully, + also test we can position our new camera class to lookat the pixel""" + #lets not look directly at the point we are drawing, demonstrating we can lookat points in space + self.camera.lookat.x = 20 + self.camera.lookat.y = 20 + self.camera.lookat.z = -20 + + #recalculate our camera based on the new settings + self.camera.update() + + glClearColor(0.0, 0.0, 0.0, 0.0) + glClear(GL_COLOR_BUFFER_BIT) + self.glwrap.draw_start() + + #place a point we can lookat that will be positioned in our field of view. + self.test_point.draw() + + self.glwrap.draw_finish() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/cad/05-cad-drawing-lines-in-3d-space.org b/bases/do-blog/resources/documents/python/cad/05-cad-drawing-lines-in-3d-space.org new file mode 100755 index 0000000..e585738 --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/05-cad-drawing-lines-in-3d-space.org @@ -0,0 +1,62 @@ +#+TITLE: Python CAD Tutorial 05 - Drawing lines in 3D space +#+DATE: 2013-10-05 12:00:00 UTC +#+DESCRIPTION: Connect our points from a previous tutorial together with lines. +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 05-cad-drawing-lines-in-3d-space +#+THUMBNAIL: ../../../images/cad/tut05-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 05 - Drawing lines in 3D space +.. slug: 05-cad-drawing-lines-in-3d-space.org +.. date: 2013-10-05 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Connect our points from a previous tutorial together with lines. +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut05][View/Download Code]] + + +#+CAPTION: Lines +[[../../../images/cad/tut05-interface.png]] + +In this tutorial we will create a new line class. Initially this will be setup to create an unlimited number of lines, drawn on every second click of the mouse.Our new class will be similar to the point class, but will contain two points to store the start and stop coordinates of the lines. We will also adjust the draw method to draw lines instead of points. + +#+BEGIN_SRC python +from point import createpoint + + +class createline: + p1 = p2 = None + color = 0, 1, 0 + display_color = (0, 1, 0) + + def __init__(self, point1, point2): + """ create the start and stop points and colour of the line""" + self.p1 = createpoint(point1) + self.p2 = createpoint(point2) + + def glvertex(self): + """ Opengl vertex useful so we can dont have to glbegin and glend for each point""" + glVertex3f(self.p1.x, self.p1.y, self.p1.z) + glVertex3f(self.p2.x, self.p2.y, self.p2.z) + + def get_points(self): + """ return the start and end point of the line could be used in a drawing loop""" + for p in (self.p1, self.p2): + yield (p) + + def draw(self): + """ lets draw the line baase on the cordinates, also draw the points for now """ + glColor3f(*self.display_color) + + glBegin(GL_LINES) + glVertex3f(self.p1.x, self.p1.y, self.p1.z) + glVertex3f(self.p2.x, self.p2.y, self.p2.z) + glEnd() + + self.p1.draw() + self.p2.draw() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/cad/06-cad-drawing-planes.org b/bases/do-blog/resources/documents/python/cad/06-cad-drawing-planes.org new file mode 100755 index 0000000..6ccf65a --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/06-cad-drawing-planes.org @@ -0,0 +1,78 @@ +#+TITLE: Python CAD Tutorial 06 - Drawing planes in 3D space +#+DATE: 2013-10-06 12:00:00 UTC +#+DESCRIPTION: Drawing planes in 3d space +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 06-cad-drawing-planes-in-3d-space +#+THUMBANIL: ../../../images/cad/tut06-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 06 - Drawing planes in 3D space +.. slug: 06-cad-drawing-planes-in-3d-space.org +.. date: 2013-10-06 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Drawing planes in 3d space +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut06][View/Download Code]] + + +#+CAPTION: Planes +[[../../../images/cad/tut06-interface.png]] + +We will now add a plane class, this is another fundamental building block. It will also be used for various collision detections at a later stage.We have created two classes below: + + + The first class will be used for the grid in the next tutorial. A plane does not normally have a limit of size but in this we are limiting it for display purposes.'createplanesimple' takes a point in space and a size. It will then calculate the plane by appending the size to the x and y coridinates, z will remain fixed. + + The second class is a more accurate way of representing a plane. Usually we only need to store a point in space and a direction, so by adding the coordinates together we can get a central point and direction this will be added at a later date. + +#+BEGIN_SRC python +from point import createpoint + +class createplanesimple: + def __init__(self, p1, plane_size=10): + size = plane_size / 2 + self.p1 = createpoint((p1[0] + size, p1[1]+size, p1[2])) + self.p2 = createpoint((p1[0] - size, p1[1]+size, p1[2])) + self.p3 = createpoint((p1[0] + size, p1[1]-size, p1[2])) + self.p4 = createpoint((p1[0] - size, p1[1]-size, p1[2])) + self.normal = createpoint((0, 1, 0)) + + def draw(self): + glBegin(GL_TRIANGLE_STRIP) + glColor3f(0.9, 0.9, 0.9) + glNormal3f(self.normal.x, self.normal.y, self.normal.z) + glVertex3f(self.p1.x, self.p1.y, self.p1.z) + glVertex3f(self.p2.x, self.p2.y, self.p2.z) + glVertex3f(self.p3.x, self.p3.y, self.p3.z) + + glVertex3f(self.p2.x, self.p2.y, self.p2.z) + glVertex3f(self.p3.x, self.p3.y, self.p3.z) + glVertex3f(self.p4.x, self.p4.y, self.p4.z) + glEnd() + self.p1.draw((0, 0, 1)) + self.p2.draw((0, 0, 1)) + self.p3.draw((0, 0, 1)) + self.p4.draw((0, 0, 1)) + + +class createplane: + def __init__(self, p1, p2, p3): + self.p1 = createpoint(p1) + self.p2 = createpoint(p2) + self.p3 = createpoint(p3) + self.normal = createpoint((0, 1, 0)) + + + def draw(self): + glBegin(GL_TRIANGLE_STRIP) + glColor3f(0.8, 0.8, 0.8) + glNormal3f(self.normal.x, self.normal.y, self.normal.z) + glVertex3f(self.p1.x, self.p1.y, self.p1.z) + glVertex3f(self.p2.x, self.p2.y, self.p2.z) + glVertex3f(self.p3.x, self.p3.y, self.p3.z) + glEnd() + self.p1.draw((1, 0, 0)) + self.p2.draw((0, 1, 0)) + self.p3.draw((0, 0, 1)) +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/cad/07-cad-drawing-a-grid.org b/bases/do-blog/resources/documents/python/cad/07-cad-drawing-a-grid.org new file mode 100755 index 0000000..1a3c2fd --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/07-cad-drawing-a-grid.org @@ -0,0 +1,81 @@ +#+TITLE: Python CAD Tutorial 07 - Drawing a grid +#+DATE: 2013-10-07 12:00:00 UTC +#+DESCRIPTION: Drawing a grid in 3d space using gl lines +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 07-cad-drawing-a-grid +#+THUMBNAIL: ../../../images/cad/tut07-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 07 - Drawing a grid +.. slug: 07-cad-drawing-a-grid.org +.. date: 2013-10-07 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Drawing a grid in 3d space using gl lines +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut07][View/Download Code]] + + +#+CAPTION: Grid +[[../../../images/cad/tut07-interface.png]] + +We created a plane in the previous tutorial. Now we will inherit that class to create a grid, at a later stage we will add snap to grid functionality.The new grid class will take three parameters: a centre point, a spacing value and a size value. We will set the size value to be the size of the viewport and will set a default spacing between the lines.The new methods for the class are below: + + + The grid_spacing method shifts the lines by the amount specified in the spacing parameter until the maximum size has been reached. + + The draw method draws lines to make up the grid. On every tenth line it changes the colour to a darker line. + +Future tutorials will deal with snap to grid functionality and auto calculating based on the zoom level, but for now we have something to work with. + +#+BEGIN_SRC python +import math + +from point import createpoint +from plane import createplanesimple + + +class creategrid: + display_color = (0.6, 0.6, 0.6) + + large_grid = 10 + small_grid = 2.5 + + def __init__(self, p1, spacing=1.5, size=65): + self.plane = createplanesimple(p1, size * 2) + self.p1 = createpoint(p1) + self.normal = createpoint((0, 1, 0)) + self.size = size + self.small_grid = spacing + self.large_grid = spacing * 10 + + def grid_spacing(self): + #work out how many times the grid units fit inside our grid size and make it a whole number + size = math.ceil(self.size / self.small_grid) * self.small_grid + #adjust x by size so we can draw the lines + x = self.p1.x - size + #loop from start until our lines are greater than our max size + while x < (self.p1.x + size): + x += self.small_grid + yield x + + def draw(self): + self.plane.draw() + glColor3f(*self.display_color) + glBegin(GL_LINES) + + for item in self.grid_spacing(): + #coordinate modulus large_grid (returns 0 if there is no remaineder), so lets draw a different colour line + if (item % self.large_grid) == 0: + glColor3f(0.4, 0.4, 0.4) + else: + glColor3f(*self.display_color) + + glVertex3f(item, self.p1.y - self.size, self.p1.z) + glVertex3f(item, self.p1.y + self.size, self.p1.z) + + glVertex3f(self.p1.x - self.size, item, self.p1.z) + glVertex3f(self.p1.x + self.size, item, self.p1.z) + glEnd() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/cad/08-cad-drawing-a-polygon-adding-shape-class.org b/bases/do-blog/resources/documents/python/cad/08-cad-drawing-a-polygon-adding-shape-class.org new file mode 100755 index 0000000..157d600 --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/08-cad-drawing-a-polygon-adding-shape-class.org @@ -0,0 +1,103 @@ +#+TITLE: Python CAD Tutorial 08 - Drawing a polygon & add generic shape class +#+DATE: 2013-10-08 12:00:00 UTC +#+DESCRIPTION: Draw a polygon and create a generic shape base class +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 08-cad-drawing-a-polygon-adding-shape-class +#+THUMBNAIL: ../../../images/cad/tut08-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 08 - Drawing a polygon & add generic shape class +.. slug: 08-cad-drawing-a-polygon-adding-shape-class.org +.. date: 2013-10-08 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Draw a polygon and create a generic shape base class +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut08][View/Download Code]] + + +#+CAPTION: Polygon +[[../../../images/cad/tut08-interface.png]] + +We created a grid in the previous tutorial. Now let's add a polygon class and a shape object to contain our primitives.The polygon class is basically an extension of the line class. It works in the same way, however we have added an append method which keeps adding points to the polygon on each click. The draw method has then been adjusted to loop over and draw the polygon from the point list. + +#+BEGIN_SRC python +from point import createpoint + + +class createpolygon: + name = 'polygon' + points = [] + closed = True + + def __init__(self, point): + self.points = [] + self.points.append(createpoint(point)) + self.color = 0, 1, 0 + self.display_color = 0, 1, 0 + self.newPoint = None + + def append(self, point): + self.points.append(createpoint(point)) + + def glvertex(self): + glVertex3f(self.p1.x, self.p1.y, self.p1.z) + + #vertexs to draw a line use with glbegin(GL_LINES) + def glvertexlist(self): + glVertex3f(self.p1.x, self.p1.y, self.p1.z) + glVertex3f(self.p2.x, self.p2.y, self.p2.z) + + def get_points(self): + for p in self.points: + yield (p) + + def draw(self): + glColor3f(*self.display_color) + if len(self.points) > 1: + glBegin(GL_LINES) + for n in xrange(0, len(self.points) - 1): + glVertex3f(self.points[n].x, self.points[n].y, self.points[n].z) + glVertex3f(self.points[n + 1].x, self.points[n + 1].y, self.points[n + 1].z) + if self.closed is True: + glVertex3f(self.points[0].x, self.points[0].y, self.points[0].z) + glVertex3f(self.points[-1].x, self.points[-1].y, self.points[-1].z) + glEnd() + + for item in self.points: + item.draw() +#+END_SRC + +The shape class below is basically just a container object for storing our various primitives like lines, circles and polygons. This class has methods to loop over and add the primitives store,. This will be useful later on for changing properties on mass.In the main mycad.py file you can see that our new polygon is contained and updated through the shape class. Later on we will attach this to our GUI so we can dynamically add primitives as we need them. + +#+BEGIN_SRC python +from point import createpoint + + +class createshape: + location = createpoint((0, 0, 0)) + primitives = [] + + def __init__(self, name=None): + self.primitives = [] + + def append(self, item): + self.primitives.append(item) + + def __getitem__(self, n): + return self.primitives[n] + + #temporary to get last primitive added to the scene + def update_last(self): + return self.primitives[-1] + + def count(self): + return len(self.primitives) - 1 + + def draw(self): + for item in self.primitives: + item.draw() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/cad/09-cad-workspace-class.org b/bases/do-blog/resources/documents/python/cad/09-cad-workspace-class.org new file mode 100755 index 0000000..e4dcd2a --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/09-cad-workspace-class.org @@ -0,0 +1,71 @@ +#+TITLE: Python CAD Tutorial 09 - Workspace class +#+DATE: 2013-10-09 12:00:00 UTC +#+DESCRIPTION: Create a workspace class to hold our objects +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 09-cad-workspace-class +#+THUMBNAIL: ../../../images/cad/tut09-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 09 - Workspace class +.. slug: 09-cad-workspace-class.org +.. date: 2013-10-09 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Create a workspace class to hold our objects +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut09][View/Download Code]] + + +#+CAPTION: workspace class +[[../../../images/cad/tut09-interface.png]] + +The workspace class below is a management class. It will contain various methods for working with the visible objects. It will also manage our layers. For now we will add simple functions, for example 'append' (which will add objects to a layer).In the main mycad.py file, you can see that our new polygon is contained and updated through the shape class. Later on we will attach this to our GUI so we can dynamically add primitives as we need them. + + +#+BEGIN_SRC python +from cad.point import createpoint +from OpenGL.GL import * +from OpenGL.arrays import vbo +from numpy import array + + +#lets create a dict object we can name our layers from here +class workspace(dict): + selected_shape = 'workspace' + selected_part = None + + def reset(self): + for k in self.keys(): + del(self[k]) + self['workspace'] = shape.createshape() + + def create(self, item, name=None): + if name is not None: + self.selected_part = name + if self.selected_part is not None: + if not self.get(self.selected_part): + self[self.selected_part] = shape.createshape() + self[self.selected_part].append(item) + self.selected_shape = self[self.selected_part].count() + + def append(self, item, name=None): + self[self.selected_part].primitives[self.selected_shape].append(item) + + def objects_iter(self): + for layer in self.keys(): + for shape in self[layer]: + yield shape + + def set(self, name, value): + for layer in self.values(): + for item in layer.primitives: + item.display_color = value + self.color = value + + #handle drawing our shapes here + def draw(self): + for item in self.values(): + item.draw() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/cad/10-cad-extending-the-interface.org b/bases/do-blog/resources/documents/python/cad/10-cad-extending-the-interface.org new file mode 100755 index 0000000..65ae686 --- /dev/null +++ b/bases/do-blog/resources/documents/python/cad/10-cad-extending-the-interface.org @@ -0,0 +1,172 @@ +#+TITLE: Python CAD Tutorial 10 - Extending the GTK interface +#+DATE: 2013-10-10 12:00:00 UTC +#+DESCRIPTION: Add new GUI elements to initiate drawing of various shapes +#+FILETAGS: GTK-3:python:cad:opengl +#+CATEGORY: python +#+SLUG: 10-cad-extending-the-interface +#+THUMBNAIL: ../../../images/cad/tut10-interface.png +#+BEGIN_COMMENT +.. title: Python CAD Tutorial 10 - Extending the GTK interface +.. slug: 10-cad-extending-the-interface.org +.. date: 2013-10-10 12:00:00 UTC +.. tags: GTK-3, python, cad, opengl +.. category: python +.. description: Add new GUI elements to initiate drawing of various shapes +.. type: text +#+END_COMMENT + +[[https://code.launchpad.net/~oly/fabricad/tut10][View/Download Code]] + + +#+CAPTION: extended gui +[[../../../images/cad/tut10-interface.png]] + +We have created some buttons in glade and connected the events so we can switch between the object types ie circle polygon and closed polygons. + +Create a class for managing the treeview, this widget is very flexible and can be difficult to work with so abstract the details away. This will be used for showing the layers we have in the project and the objects under each layer. + +#+BEGIN_SRC python +import os +import sys +from gi.repository import Gtk, GdkX11, Gdk + + +class objectList: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, workspace, treeview): + self.treeview = treeview + self.workspace = workspace + + # connect the treeview events, capture row selection and row click + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.show_menu) + + #add a tree store model so everything has a hierarchy + self.treemodel = Gtk.TreeStore(str) + self.treeview.set_model(self.treemodel) + + # append colmun headers to the treeview and cell renderer + column = Gtk.TreeViewColumn("Objects") + self.treeview.append_column(column) + + cell = Gtk.CellRendererText() + column.pack_start(cell, False) + column.add_attribute(cell, "text", 0) + + # populate the objects from the layers + self.populate() + self.menu() + + def populate(self): + self.treemodel.clear() + for layer in self.workspace.keys(): + tree_iter = self.append(layer) + for part in self.workspace[layer]: + self.append(part.name, tree_iter) + + def menu(self): + self.layer_menu = Gtk.Menu() + + self.layer_menu_item = Gtk.MenuItem("Display") + #self.layer_menu_item.connect("activate", self.toggle_value_handler, 'show') + self.layer_menu.append(self.layer_menu_item) + + self.layer_menu_item = Gtk.MenuItem("Colour") + self.layer_menu.append(self.layer_menu_item) + + def show_menu(self, tv, event): + if event.button == 2: + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + + if event.button == 3: + self.layer_menu.show_all() + self.layer_menu.popup(None, None, None, None, 1, 0) + + def append(self, name, parent=None): + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def selection(self, tv, treepath, tvcolumn): + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) +#+END_SRC + +The workspace class below is a management class. It will contain various methods for working with the visible objects. It will also manage our layers. For now we will add simple functions, for example 'append' (which will add objects to a layer).In the main mycad.py file, you can see that our new polygon is contained and updated through the shape class. Later on we will attach this to our GUI so we can dynamically add primitives as we need them. + +#+BEGIN_SRC python +class objectList: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, workspace, treeview): + self.treeview = treeview + self.workspace = workspace + + # connect the treeview events, capture row selection and row click + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.show_menu) + + #add a tree store model so everything has a hierarchy + self.treemodel = Gtk.TreeStore(str) + self.treeview.set_model(self.treemodel) + + # append colmun headers to the treeview and cell renderer + column = Gtk.TreeViewColumn("Objects") + self.treeview.append_column(column) + + cell = Gtk.CellRendererText() + column.pack_start(cell, False) + column.add_attribute(cell, "text", 0) + + # populate the objects from the layers + self.populate() + self.menu() + + def populate(self): + self.treemodel.clear() + for layer in self.workspace.keys(): + tree_iter = self.append(layer) + for part in self.workspace[layer]: + self.append(part.name, tree_iter) + + def menu(self): + self.layer_menu = Gtk.Menu() + + self.layer_menu_item = Gtk.MenuItem("Display") + #self.layer_menu_item.connect("activate", self.toggle_value_handler, 'show') + self.layer_menu.append(self.layer_menu_item) + + self.layer_menu_item = Gtk.MenuItem("Colour") + self.layer_menu.append(self.layer_menu_item) + + def show_menu(self, tv, event): + if event.button == 2: + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + + if event.button == 3: + self.layer_menu.show_all() + self.layer_menu.popup(None, None, None, None, 1, 0) + + def append(self, name, parent=None): + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def selection(self, tv, treepath, tvcolumn): + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/01-displaying-a-window.org b/bases/do-blog/resources/documents/python/gtk3/01-displaying-a-window.org new file mode 100755 index 0000000..af2e1c1 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/01-displaying-a-window.org @@ -0,0 +1,52 @@ +#+TITLE: Display a GTK window +#+DATE: 2014-01-05 12:00:00 UTC +#+DESCRIPTION: GTK Example on loading a window from a glade fiel and displaying it on the screen. +#+FILETAGS: GTK-3:python:window +#+CATEGORY: python +#+SLUG: 01-displaying-a-gtk-window +#+THUMBNAIL:../../../images/gtk/tut01-windows.png +#+BEGIN_COMMENT +.. title: Display a GTK window +.. slug: 01-displaying-a-gtk-window +.. date: 2014-01-05 12:00:00 UTC +.. tags: GTK-3, python, window +.. category: python +.. description: GTK Example on loading a window from a glade fiel and displaying it on the screen. +.. type: text +#+END_COMMENT + + +#+CAPTION: Display GTK-3 window +[[../../../images/gtk/tut01-windows.png]] + + +Simple GTK application load a window from a glade file and exit on hitting the window close button, we use the get_object method to get the window by name then use connect to attach to the close and destroy events. + +There are lots of event we can connect to like mouse click key press and window minimise maximise check the gtk docs or glade for a list of possible event for each widget. + +#+BEGIN_SRC python +#!/usr/bin/python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 01 Create and destroy a window""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut01.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + +application = application_gui() +Gtk.main() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/gtk3/02-adding-buttons-and-switches.org b/bases/do-blog/resources/documents/python/gtk3/02-adding-buttons-and-switches.org new file mode 100755 index 0000000..8fdd0d2 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/02-adding-buttons-and-switches.org @@ -0,0 +1,70 @@ +#+TITLE: Adding GTK-3 buttons and switches +#+DATE: 2014-01-08 12:00:00 UTC +#+DESCRIPTION: GTK Example on click and toggle buttons connect to there events to run code when activated by the user. +#+FILETAGS: GTK-3:python:glade +#+CATEGORY: python +#+SLUG: 02-adding-buttons-and-switches +#+THUMBNAIL:../../../images/gtk/tut02-buttons.png +#+BEGIN_COMMENT +.. title: Adding GTK-3 buttons and switches +.. slug: 02-adding-buttons-and-switches +.. date: 2014-01-08 12:00:00 UTC +.. tags: GTK-3, python, glade +.. category: python +.. description: GTK Example on click and toggle buttons connect to there events to run code when activated by the user. +.. type: text +#+END_COMMENT + + +#+CAPTION: Display GTK-3 Buttons and switches +[[../../../images/gtk/tut02-buttons.png]] + + +The example below loads 4 buttons from a glade 2 standard and 2 toggle buttons, it then connects event handles to show some text when clicked or to show the toggle state if a toggle button was clicked. + +#+BEGIN_SRC python +#!/usr/bin/python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 02 buttons""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut02-buttons.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + self.buttons = {} + self.buttons['but1'] = xml.get_object('button1') + self.buttons['but2'] = xml.get_object('button2') + self.buttons['but3'] = xml.get_object('togglebutton1') + self.buttons['but4'] = xml.get_object('togglebutton2') + + self.buttons['but1'].connect('clicked', self.button_events) + self.buttons['but2'].connect('clicked', self.button_events) + self.buttons['but3'].connect('clicked', self.button_events) + self.buttons['but4'].connect('clicked', self.button_events) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def button_events(self, widget): + toggle_value = '' + if widget.get_name() == 'GtkToggleButton': + toggle_value = str(widget.get_active()) + self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value) + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/03-adding-radio-buttons-and-switches.org b/bases/do-blog/resources/documents/python/gtk3/03-adding-radio-buttons-and-switches.org new file mode 100755 index 0000000..b2910c6 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/03-adding-radio-buttons-and-switches.org @@ -0,0 +1,80 @@ +#+TITLE: Adding switch and radio widgets +#+DATE: 2014-01-10 12:00:00 UTC +#+DESCRIPTION: GTK example demonstrating the use of switches and radio buttons. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 03-adding-radio-buttons-and-switches +#+THUMBNAIL:../../../images/gtk/tut03-switch-radio.png +#+BEGIN_COMMENT +.. title: Adding switch and radio widgets +.. slug: 03-adding-radio-buttons-and-switches +.. date: 2014-01-10 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: GTK example demonstrating the use of switches and radio buttons. +.. type: text +#+END_COMMENT + + +#+CAPTION: Adding radio buttons & switches +[[../../../images/gtk/tut03-switch-radio.png]] + + +The below sample shows loading and retrieving state values for radio buttons and switches. + +#+BEGIN_SRC python +#!/usr/bin/python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 03 Radio Buttons and switches""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut03.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + self.buttons = {} + self.buttons['radio1'] = xml.get_object('radiobutton1') + self.buttons['radio2'] = xml.get_object('radiobutton2') + self.buttons['switch1'] = xml.get_object('switch1') + self.buttons['switch2'] = xml.get_object('switch2') + + #set a name of the switches because they dont have a label so we can use this to show selected button + self.buttons['switch1'].set_name('switch1') + self.buttons['switch2'].set_name('switch2') + + #connect the interface events to functions + self.buttons['switch1'].connect('button-press-event', self.switch_button_events) + self.buttons['switch2'].connect('button-press-event', self.switch_button_events) + self.buttons['radio1'].connect('toggled', self.radio_button_events) + self.buttons['radio2'].connect('toggled', self.radio_button_events) + + #add the second radio to the first radio button making it part of the group + self.buttons['radio2'].join_group(self.buttons['radio1']) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def switch_button_events(self, widget, params): + toggle_value = str(widget.get_active()) + self.text.set_text(widget.get_name() + ' ' + toggle_value) + + def radio_button_events(self, widget): + toggle_value = str(widget.get_active()) + self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value) + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/04-adding-dropdowns-and-spin-buttons.org b/bases/do-blog/resources/documents/python/gtk3/04-adding-dropdowns-and-spin-buttons.org new file mode 100755 index 0000000..3882198 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/04-adding-dropdowns-and-spin-buttons.org @@ -0,0 +1,106 @@ +#+TITLE: Adding Dropdowns and spin buttons +#+DATE: 2014-01-15 12:00:00 UTC +#+DESCRIPTION: GTK example of some combo / slecet drop down boxesfor user interaction. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 04-adding-dropdowns-and-spin-buttons +#+THUMBNAIL:../../../images/gtk/tut04-text-dropdown-spin.png +#+BEGIN_COMMENT +.. title: Adding Dropdowns and spin buttons +.. slug: 04-adding-dropdowns-and-spin-buttons +.. date: 2014-01-15 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: GTK example of some combo / slecet drop down boxesfor user interaction. +.. type: text +#+END_COMMENT + + +#+CAPTION: Adding dropdown & spin widgets +[[../../../images/gtk/tut04-text-dropdown-spin.png]] + + + +This example demonstrates using some drop down boxes and adding new items and retrieving the selected values. +This snippet also demonstrates the use of spin buttons for selecting a value from a range. + +When creating a combobbox in glade make sure you add a liststore to the widget, and also edit the combobox properties in a seperate window so you can access and the hierarchy menu and assign a cell render to the column in your listview. + +#+CAPTION: Adding a list store +[[../../../images/gtk/add-list-store.png]] + +#+CAPTION: Adding a cell renderer +[[../../../images/gtk/attach-cell-renderer-set-text-column.png]] + +#+CAPTION: Adding a list store +[[../../../images/gtk/attach-liststore-model.png]] + +#+CAPTION: Adding a combobox list store +[[../../../images/gtk/edit-combobox-liststore.png]] + + +#+BEGIN_SRC python +#!/usr/bin/env python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 04 text input, spin input, drop down options""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut04.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['spin1'] = xml.get_object('spinbutton1') + self.widgets['spin2'] = xml.get_object('spinbutton2') + + #simple dropdown just text + self.widgets['comboboxtext'] = xml.get_object('comboboxtext1') + + #more complex multi row multi data types in dropdown using renderers + self.widgets['combobox'] = xml.get_object('combobox1') + self.widgets['combobox_liststore']= xml.get_object('liststore1') + + #add some items to the drop down + self.widgets['combobox_liststore'].append(['new row test']) + self.widgets['combobox_liststore'].append(['second row test']) + + #adding new rows to the dropdown + self.widgets['comboboxtext'].append_text('row4') + self.widgets['comboboxtext'].append_text('row5') + + #connect to events + self.widgets['comboboxtext'].connect('changed', self.dropdown_event_text) + self.widgets['spin1'].connect('value-changed', self.spin_button_event) + self.widgets['spin2'].connect('value-changed', self.spin_button_event) + self.widgets['combobox'].connect('changed', self.dropdown_event) + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show_all() + + def dropdown_event_text(self, widget): + self.text.set_text(widget.get_active_text()) + + def dropdown_event(self, widget): + list_view_model = widget.get_model() + active_iter_index = widget.get_active() + row_iter = list_view_model.get_iter(active_iter_index) + self.text.set_text(widget.get_name() + ' ' +list_view_model.get_value(row_iter, 0 )) + + def spin_button_event(self, widget): + self.text.set_text(widget.get_name() + ' ' + ' ' + str(widget.get_value())) + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/05-adding-progress-bars-and-spinners.org b/bases/do-blog/resources/documents/python/gtk3/05-adding-progress-bars-and-spinners.org new file mode 100755 index 0000000..bd2a807 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/05-adding-progress-bars-and-spinners.org @@ -0,0 +1,91 @@ +#+TITLE: Adding progress bars & spinners +#+DATE: 2014-01-20 12:00:00 UTC +#+DESCRIPTION: Load a progress bar widget and spinner to demonstrate updating and retriving values. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 05-adding-progress-bars-and-spinners +#+THUMBNAIL:../../../images/gtk/tut05-progress-spinners.png +#+BEGIN_COMMENT +.. title: Adding progress bars & spinners +.. slug: 05-adding-progress-bars-and-spinners +.. date: 2014-01-20 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a progress bar widget and spinner to demonstrate updating and retriving values. +.. type: text +#+END_COMMENT + + +#+CAPTION: Progress bars and spinners +[[../../../images/gtk/tut05-progress-spinners.png]] + +This sample demonstrates two progress bar styles and how to update them too show progress, it also demonstrates starting and stopping a spinning graphic. + +#+BEGIN_SRC python +#!/usr/bin/env python +from gi.repository import Gtk, GLib + + +class application_gui: + """Tutorial 05 demo progress bars and the spinner graphic.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut05.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['entry'] = xml.get_object('entry2') + + self.widgets['spinner1'] = xml.get_object('spinner1') + self.widgets['spinnerbutton'] = xml.get_object('togglebutton1') + self.widgets['progress1'] = xml.get_object('progressbar1') + self.widgets['progress2'] = xml.get_object('progressbar2') + + self.widgets['spinnerbutton'].connect('clicked', self.toggle_spinner) + + self.widgets['progress1'].set_text('active mode') + self.widgets['progress2'].set_text('percentage mode') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + self.widgets['spinner1'].start() + + GLib.timeout_add_seconds(1, self.update_active_progress_bar) + GLib.timeout_add_seconds(1, self.update_percentage_progress_bar) + + def update_active_progress_bar(self): + # nudge the progress bar to show something is still happening, just updating every second in the example + # long running processes which have an unknow finish time would use this version + self.widgets['progress1'].pulse() + return True + + def update_percentage_progress_bar(self): + self.count +=0.1 + self.widgets['progress2'].set_fraction(self.count) + if self.count<1: + return True + else: + return False + + def toggle_spinner(self, widget): + if widget.get_active(): + self.widgets['spinner1'].start() + else: + self.widgets['spinner1'].stop() + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/06-dialog-boxes.org b/bases/do-blog/resources/documents/python/gtk3/06-dialog-boxes.org new file mode 100755 index 0000000..07916ab --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/06-dialog-boxes.org @@ -0,0 +1,112 @@ +#+TITLE: Displaying dialog boxes +#+DATE: 2014-02-03 12:00:00 UTC +#+DESCRIPTION: Demonstration of opening various dialog boxes and retrieving the selected values. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 06-dialog-boxes +#+THUMBNAIL:../../../images/gtk/tut06-dialogs.png +#+BEGIN_COMMENT +.. title: Displaying dialog boxes +.. slug: 06-dialog-boxes +.. date: 2014-02-03 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Demonstration of opening various dialog boxes and retrieving the selected values. +.. type: text +#+END_COMMENT + + +#+CAPTION: Dialog Boxes +[[../../../images/gtk/tut06-dialogs.png]] + + +This example program allows you to open the file selector dialogs and displays the selected files, it also demonstrates selecting colour and font from the built in selector dialogs. +once a file font or colour has been selected we grab the resulting value and display it as an example of retrieving the value. + + +#+BEGIN_SRC python +#!/usr/bin/env python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 06 demo of file, colour and font dialogs.""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut06.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['entry'] = xml.get_object('entry2') + self.widgets['color_button'] = xml.get_object('btnColour') + self.widgets['font_button'] = xml.get_object('btnFont') + self.widgets['open_button'] = xml.get_object('btnLoad') + self.widgets['save_button'] = xml.get_object('btnSave') + + self.widgets['color_button'].connect('clicked', self.colour_selector) + self.widgets['font_button'].connect('clicked', self.font_selector) + self.widgets['save_button'].connect('clicked', self.save_file_selector) + self.widgets['open_button'].connect('clicked', self.open_file_selector) + + self.widgets['open_dialog'] = xml.get_object('fileChooserLoad') + self.widgets['open_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE) + self.widgets['open_dialog'].add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK) + + self.widgets['save_dialog'] = xml.get_object('fileChooserSave') + self.widgets['save_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE) + self.widgets['save_dialog'].add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.OK) + + self.widgets['color_dialog'] = xml.get_object('colorSelect') + + self.widgets['font_dialog'] = xml.get_object('fontSelect') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def colour_selector(self, widget): + self.widgets['color_dialog'].run() + self.colour = self.widgets['color_dialog'].get_color_selection().get_current_color() + + #accessing the individual values as integers, converting to floats here + colour = [self.colour.red / 65535.0, + self.colour.green / 65535.0, + self.colour.blue / 65535.0] + + self.widgets['color_dialog'].hide() + self.text.set_text(self.widgets['color_dialog'].get_name() + ' ' + self.colour.to_string()) + + def font_selector(self, widget): + self.widgets['font_dialog'].run() + self.font = self.widgets['font_dialog'].get_font_selection() + self.text.set_text(self.widgets['font_dialog'].get_name() + ' ' + self.font.get_font_name() + ' ' + str(self.font.get_size())) + self.widgets['font_dialog'].hide() + + def open_file_selector(self, widget): + response = self.widgets['open_dialog'].run() + if self.widgets['open_dialog'].get_filenames(): + self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ str(self.widgets['open_dialog'].get_filenames())) + else: + if self.widgets['open_dialog'].get_filename(): + self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ self.widgets['open_dialog'].get_filename()) + self.widgets['open_dialog'].hide() + + def save_file_selector(self, widget): + response = self.widgets['save_dialog'].run() + self.text.set_text(self.widgets['save_dialog'].get_name() + ' '+ self.widgets['save_dialog'].get_filename()) + self.widgets['save_dialog'].hide() + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/07-app-chooser-scale-button.org b/bases/do-blog/resources/documents/python/gtk3/07-app-chooser-scale-button.org new file mode 100755 index 0000000..0c7b218 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/07-app-chooser-scale-button.org @@ -0,0 +1,80 @@ +#+TITLE: App chooer and scale buttons +#+DATE: 2014-02-07 12:00:00 UTC +#+DESCRIPTION: Load a glade file displaying some scale buttons get value on change, displays some app selector widgets and open the applications. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 07-app-chooser-scale-button +#+THUBNAIL:../../../images/gtk/tut07-appchooser-scale.png +#+BEGIN_COMMENT +.. title: App chooer and scale buttons +.. slug: 07-app-chooser-scale-button +.. date: 2014-02-07 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a glade file displaying some scale buttons get value on change, displays some app selector widgets and open the applications. +.. type: text +#+END_COMMENT + +#+CAPTION: App chooser & scale widgets +[[../../../images/gtk/tut07-appchooser-scale.png]] + + +This example program demonstrates the use of appchooser buttons when selecting an application from the drop down launch the application loading a test text file, this could be a video or mp3 or any file type. + +you can change the application list by modify the content type value in glade this then shows all registered apps for that content type. + +#+BEGIN_SRC python +#!/usr/bin/env python +from gi.repository import Gtk, GLib, Gio + + +class application_gui: + """Tutorial 04 text input, spin input, drop down options""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut07.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['scale1'] = xml.get_object('scalebutton1') + self.widgets['scale2'] = xml.get_object('scalebutton2') + self.widgets['appchooseraudio'] = xml.get_object('appchooserbutton1') + self.widgets['appchoosertext'] = xml.get_object('appchooserbutton2') + + self.widgets['appchooseraudio'].connect('changed', self.app_chooser) + self.widgets['appchoosertext'].connect('changed', self.app_chooser) + self.widgets['scale1'].connect('value-changed', self.scale) + self.widgets['scale2'].connect('value-changed', self.scale) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def app_chooser(self, widget): + list_view_model = widget.get_model() + active_iter_index = widget.get_active() + row_iter = list_view_model.get_iter(active_iter_index) + app_info = list_view_model.get_value(row_iter, 0) + + gio_file = Gio.File.new_for_path('/tmp/tut07-appchooser-test.txt') + app_info.launch((gio_file,), None) + self.text.set_text(widget.get_name() + ' ' + app_info.get_name()) + + def scale(self, widget, value): + self.text.set_text(widget.get_name() + ' ' + str(value)) + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/08-calendars-and-menus.org b/bases/do-blog/resources/documents/python/gtk3/08-calendars-and-menus.org new file mode 100755 index 0000000..16d46a7 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/08-calendars-and-menus.org @@ -0,0 +1,74 @@ +#+TITLE: GTK-3 Calendars and menus +#+DATE: 2014-03-08 12:00:00 UTC +#+DESCRIPTION: Load a glade file displaying a menu bar and calendar widget. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 08-calendars-and-menus +#+THUMBNAIL: ../../../images/gtk/tut08-menu-calendar.png +#+BEGIN_COMMENT +.. title: GTK-3 Calendars and menus +.. slug: 08-calendars-and-menus +.. date: 2014-03-08 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a glade file displaying a menu bar and calendar widget. +.. type: text +#+END_COMMENT + +#+CAPTION: Calendar & menus +[[../../../images/gtk/tut08-menu-calendar.png]] + +Simple GTK application demonstrating the use of the date picker and application menus. The menu is hooked upto to show and hide the calendar and to set the text entry field to say hello world. Double clicking a day in the calendar widget will show the date in the text entry field. + +#+BEGIN_SRC python +#!/usr/bin/env python +from gi.repository import Gtk, GLib, GObject + + +class application_gui: + """Tutorial 08 menu, calendar widget.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut08.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['calendar'] = xml.get_object('calendar1') + self.widgets['menushow'] = xml.get_object('menuitem6') + self.widgets['menuhide'] = xml.get_object('menuitem7') + self.widgets['menuhello'] = xml.get_object('menuitem8') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + self.widgets['menushow'].connect('activate', self.showcalendar) + self.widgets['menuhide'].connect('activate', self.hidecalendar) + self.widgets['menuhello'].connect('activate', self.hello) + self.widgets['calendar'].connect('day-selected', self.date_selected) + + #show the window else there is nothing to see :) + self.window.show() + + def hidecalendar(self, *args): + self.widgets['calendar'].hide() + + def showcalendar(self, *args): + self.widgets['calendar'].show() + + def hello(self, *args): + self.text.set_text('hello world') + + def date_selected(self, *args): + date = self.widgets['calendar'].get_date() + self.text.set_text(str(date[2]) + '-'+str(date[1]) + '-' + str(date[0])) + +application = application_gui() +Gtk.main() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/gtk3/09-treeview-liststore-mode.org b/bases/do-blog/resources/documents/python/gtk3/09-treeview-liststore-mode.org new file mode 100755 index 0000000..19205e3 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/09-treeview-liststore-mode.org @@ -0,0 +1,141 @@ +#+TITLE: GTK-3 Treeview in liststore mode +#+DATE: 2014-04-12 12:00:00 UTC +#+DESCRIPTION: Example of using a treeview, the treeview display can be adjusted using different store models in this case we use the liststore model. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 09-treeview-liststore-mode +#+THUMBNAIL: ../../../images/gtk/tut09-treeview-listview.png +#+BEGIN_COMMENT +.. title: GTK-3 Treeview in liststore mode +.. slug: 09-treeview-liststore-mode +.. date: 2014-04-12 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Example of using a treeview, the treeview display can be adjusted using different store models in this case we use the liststore model. +.. type: text +#+END_COMMENT + +#+CAPTION: Treeview in list mode +[[../../../images/gtk/tut09-treeview-listview.png]] + +The below example demonstrates using the treeview to display table style data, it generates a few rows and shows row selection from a mouse click.") + +#+BEGIN_SRC python +#!/usr/bin/env python +from gi.repository import Gtk, GLib, GObject + + +class application_gui: + """Tutorial 09 treeview in list mode.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut09.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['treeview'] = xml.get_object('treeview1') + treeview(self.widgets['treeview'], self.text) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + +class treeview: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, treeview, entry): + self.entry = entry + self.treeview = treeview + + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.mouse_click) + + #create a storage model in this case a treemodel + self.treemodel = Gtk.ListStore(str, str, str) + self.treeview.set_model(self.treemodel) + + #add columns usually only one in case of the treeview + column1 = Gtk.TreeViewColumn("Column 01") + self.treeview.append_column(column1) + + column2 = Gtk.TreeViewColumn("Column 02") + self.treeview.append_column(column2) + + column3 = Gtk.TreeViewColumn("Column 03") + self.treeview.append_column(column3) + + #add in a text renderer so we can see the items we add + cell = Gtk.CellRendererText() + column1.pack_start(cell, False) + column1.add_attribute(cell, "text", 0) + + cell = Gtk.CellRendererText() + column2.pack_start(cell, False) + column2.add_attribute(cell, "text", 0) + + cell = Gtk.CellRendererText() + column3.pack_start(cell, False) + column3.add_attribute(cell, "text", 0) + + self.populate() + self.menu() + + def populate(self): + self.treemodel.clear() + #populate the treeview with some list items + for item1 in range(0, 5): + iter_level_1 = self.append_tree('Item ' + str(item1)) + + def append_tree(self, name, parent=None): + """ + append to the treeview if parent is null append to root level. + if parent is a valid iter (possibly returned from previous append) then append under the parent + """ + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def menu(self): + """ + popover menu shown on right clicking a treeview item. + """ + self.treeview_menu = Gtk.Menu() + for item in range(0, 5): + menu_item = Gtk.MenuItem("Menu " + str(item)) + self.treeview_menu.append(menu_item) + + def mouse_click(self, tv, event): + if event.button == 3: + # right mouse button pressed popup the menu + self.treeview_menu.show_all() + self.treeview_menu.popup(None, None, None, None, 1, 0) + + def selection(self, tv, treepath, tvcolumn): + """ + on double click get the value of the item we clicked + """ + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + self.entry.set_text(self.selected) + + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/10-treeview-treestore-mode.org b/bases/do-blog/resources/documents/python/gtk3/10-treeview-treestore-mode.org new file mode 100755 index 0000000..071626d --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/10-treeview-treestore-mode.org @@ -0,0 +1,132 @@ +#+TITLE: GTK-3 Treeview in treestore mode +#+DATE: 2014-04-14 12:00:00 UTC +#+DESCRIPTION: Load a glade file displaying a collection of values in a treeview using the treestore hierarchy. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 10-treeview-treestore-mode +#+THUMBNAIL: ../../../images/gtk/tut10-treeview-treestore.png +#+BEGIN_COMMENT +.. title: GTK-3 Treeview in treestore mode +.. slug: 10-treeview-treestore-mode +.. date: 2014-04-14 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a glade file displaying a collection of values in a treeview using the treestore hierarchy. +.. type: text +#+END_COMMENT + +#+CAPTION: Treeview in tree mode +[[../../../images/gtk/tut10-treeview-treestore.png]] + + +The code below will populate the treeview widget with a tree like structure which you can expand and collapse, double clicking will retrieve the selected node in the tree. + +#+BEGIN_SRC python +#!/usr/bin/env python +from gi.repository import Gtk, GLib + + +class application_gui: + """Tutorial 10 text input, display a treeview in expandable tree format.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut10.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['treeview'] = xml.get_object('treeview1') + treeview(self.widgets['treeview'], self.text) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + +class treeview: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, treeview, entry): + self.entry = entry + self.treeview = treeview + + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.mouse_click) + + #create a storage model in this case a treemodel + self.treemodel = Gtk.TreeStore(str) + self.treeview.set_model(self.treemodel) + + #add columns usually only one in case of the treeview + column = Gtk.TreeViewColumn("Objects") + self.treeview.append_column(column) + + #add in a text renderer so we can see the items we add + cell = Gtk.CellRendererText() + column.pack_start(cell, False) + column.add_attribute(cell, "text", 0) + + self.populate() + self.menu() + + def populate(self): + self.treemodel.clear() + #populate the treeview with a largish tree + for item1 in range(0, 5): + iter_level_1 = self.append_tree('Item ' + str(item1)) + for item2 in range(0, 5): + iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1) + for item3 in range(0, 5): + self.append_tree('Sub Sub Item ' + str(item3), iter_level_2) + + def append_tree(self, name, parent=None): + """ + append to the treeview if parent is null append to root level. + if parent is a valid iter (possibly returned from previous append) then append under the parent + """ + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def menu(self): + """ + popover menu shown on right clicking a treeview item. + """ + self.treeview_menu = Gtk.Menu() + for item in range(0, 5): + menu_item = Gtk.MenuItem("Menu " + str(item)) + self.treeview_menu.append(menu_item) + + def mouse_click(self, tv, event): + if event.button == 3: + # right mouse button pressed popup the menu + self.treeview_menu.show_all() + self.treeview_menu.popup(None, None, None, None, 1, 0) + + def selection(self, tv, treepath, tvcolumn): + """ + on double click get the value of the item we clicked + """ + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + self.entry.set_text(self.selected) + + + +application = application_gui() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/11-interactive-python-textview.org b/bases/do-blog/resources/documents/python/gtk3/11-interactive-python-textview.org new file mode 100755 index 0000000..a332374 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/11-interactive-python-textview.org @@ -0,0 +1,235 @@ +#+TITLE: GTK-3 Textview interactions +#+DATE: 2014-04-18 12:00:00 UTC +#+DESCRIPTION: Load a glade file displaying a textview widget which runs python code on carriage return. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: 11-interactive-python-textview +#+THUMBNAIL: ../../../images/gtk/tut11-interactive-python-textview.png +#+BEGIN_COMMENT +.. title: GTK-3 Textview interactions +.. slug: 11-interactive-python-textview +.. date: 2014-04-18 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a glade file displaying a textview widget which runs python code on carriage return. +.. type: text +#+END_COMMENT + +#+CAPTION: Textview widget +[[../../../images/gtk/tut11-interactive-python-textview.png]] + + +This example shows using the text view widget to get text entered by a use, it makes use of marked text blocks so that only some text can be edited and will dynamically read the current line and run it in python interactively. + +You can type app.test() or app.show_drawing(True) as examples of the interaction between the widget and python. + +#+BEGIN_SRC python +#!/usr/bin/env python +import sys +from StringIO import StringIO +from gi.repository import Gtk, Gdk +import code +import math + + +class interactiveGtk: + + def __init__(self): + window = Gtk.Window() + window.set_default_size(380, 300) + window.connect("destroy", lambda w: Gtk.main_quit()) + + box = Gtk.VBox() + + self.drawingarea = Gtk.DrawingArea() + + #python console using a textview + console = interactive_console(Gtk.TextView()) + self.drawingarea.connect("draw", self.area_expose_cb) + + box.add(self.drawingarea) + box.add(console.textarea) + + window.add(box) + window.show_all() + + self.drawarea = False + + def show_drawing(self, state): + """self.show_drawing(True) to enable showing the lines""" + self.drawarea = state + + def test(self): + """run app.test() when program is running to print this message""" + print ('hello world') + + def area_expose_cb(self, widget, context): + """expose event lets draw, lines will only display if we run self.show_lines first. + demonstrating running state change of our program""" + self.style = self.drawingarea.get_style() + if self.drawarea is True: + self.drawing(context, 210, 10) + + def drawing(self, cr, x, y): + """ draw a circle in the drawing area """ + cr.set_line_width(10) + cr.set_source_rgb(0.5, 0.8, 0.0) + + cr.translate(20 / 2, 20 / 2) + cr.arc(50, 50, 50, 0, 2 * math.pi) + cr.stroke_preserve() + + cr.set_source_rgb(0.3, 0.4, 0.4) + cr.fill() + + +class interactive_console: + editor_chars = '>>>' + editor_chars_other = '...' + editor_history = [] + editor_history_position = 0 + + def __init__(self, textview): + #the python editor window + self.textarea = textview + self.textarea.connect('key-press-event', self.key_pressed) + self.console_buffer = self.textarea.get_buffer() + + #setup some characters which can not be changed + self.console_buffer.set_text(self.editor_chars + 'app.show_drawing(True)') + self.console_buffer.create_tag("uneditable", editable=False, editable_set=True) + self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True) + self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False) + self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(len(self.editor_chars))) + + #interactive mode interpreter, + #pass locals() or globals() so we can access our programs functions and variables + #using global here so we have access to the app object in the global scope + self.interpreter = code.InteractiveInterpreter(globals()) + + def key_pressed(self, widget, event): + """ + grab key presses, run code from textview on return + navigate history if arrow keys are pressed + """ + if event.keyval == Gdk.keyval_from_name('Return'): + self.execute_line() + return True + + if event.keyval == Gdk.keyval_from_name('Up'): + self.console_history(-1) + return True + + if event.keyval == Gdk.keyval_from_name('Down'): + self.console_history(1) + return True + return False + + def execute_line(self): + """ + carriage return was captured so lets process the textview contents for code to run + """ + + text = self.console_buffer.get_text(self.console_buffer.get_start_iter(), self.console_buffer.get_end_iter(), False) + source = '' + block = False + indent = 0 + #work out code to run if its not a block or a blank line then run what we have, + #if its a block of code like a for loop or if condition dont run it yet unless the block has finished. + last_line = '' + for line in text.split("\n"): + line = line[3:].strip('') + if line.strip() == '': + block = False + indent = 0 + else: + if line.endswith(':'): + if block is True: + source += line + "\n\t" + else: + source = line + "\n\t" + block = True + indent += 1 + else: + if line.startswith("\t"): + source += line + "\n" + else: + block = False + source = line + "\n" + indent = 0 + last_line = line + if last_line.strip() != '': + self.append_history(last_line) + chars = self.editor_chars + + + # run the code grabbed from the text buffer and execute it if its not a block + results = '' + if block is True: + chars = self.editor_chars_other + else: + chars = self.editor_chars + results = self.execute_code(source) + + #build text for the editor, and workout which part of the text should be locked + text_output = text + '\n' + results + chars + text_output_length = len(text_output) + if block is True: + text_output += "\t" * indent + self.update_editor(text_output, text_output_length) + + def execute_code(self, source): + """ + run any code sent here and capture output and return the results + """ + # capture output from stdio so we can display the errors in our editor + result = StringIO() + sys.stdout = result + sys.stderr = result + #run code output will be put into result because we are capturing stdout + self.interpreter.runsource(source, "<>") + # restore stdout so future output is capture to the terminal again + sys.stdout = sys.__stdout__ + sys.stdout = sys.__stderr__ + return result.getvalue() + + def update_editor(self, text, uneditable_end=0): + """ + pass in text to put in the editor and portion to be locked from editing + """ + self.console_buffer.set_text(text) + self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True) + self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False) + self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(uneditable_end)) + + def append_history(self, line): + """ + Store command in history drop command if limit has been reached + """ + if len(self.editor_history) > 10: + self.editor_history.pop() + self.editor_history.append(line) + self.editor_history_position = len(self.editor_history) + + def console_history(self, direction=-1): + """ + drop any text on last line and insert text from history based on position + """ + # get current text excluding last line as we will update from our history + text = self.console_buffer.get_text( + self.console_buffer.get_start_iter(), + self.console_buffer.get_iter_at_line_index( + self.console_buffer.get_line_count(), 0), + False) + + #work out position in history and what should be displayed + linenumber = self.editor_history_position + direction + if linenumber >= 0 and linenumber < len(self.editor_history): + self.editor_history_position += direction + self.console_buffer.set_text(text + self.editor_chars + self.editor_history[self.editor_history_position]) + + +app = interactiveGtk() +Gtk.main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/12-displaying-opengl-inside-drawing-area.org b/bases/do-blog/resources/documents/python/gtk3/12-displaying-opengl-inside-drawing-area.org new file mode 100755 index 0000000..2048dc3 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/12-displaying-opengl-inside-drawing-area.org @@ -0,0 +1,153 @@ +#+TITLE: GTK-3 OpenGL inside a drawing area +#+DATE: 2014-04-20 12:00:00 UTC +#+DESCRIPTION: Load a glade file displaying a drawing area widget and setup a context for opengl. +#+FILETAGS: GTK-3:python:widgets:opengl +#+CATEGORY: python +#+SLUG: 12-dispaying-opengl-inside-drawing-area +#+THUMBNAIL: ../../../images/gtk/tut12-mixing-opengl-with-gtk.png +#+BEGIN_COMMENT +.. title: GTK-3 OpenGL inside a drawing area +.. slug: 12-dispaying-opengl-inside-drawing-area +.. date: 2014-04-20 12:00:00 UTC +.. tags: GTK-3, python, widgets, opengl +.. category: python +.. description: Load a glade file displaying a drawing area widget and setup a context for opengl. +.. type: text +#+END_COMMENT + +#+CAPTION: Textview widget +[[../../../images/gtk/tut12-mixing-opengl-with-gtk.png]] + +Example of rendering opengl inside a gtk drawing area. I have seperated out the opengl code from the main gtk widget this makes it simpler to use in your own applications. + + +The example below will draw a basic opengl triangle so you know everything is setup and working, it also attaches to the drawing area resize events so you can resize the window. + +#+BEGIN_SRC python +#!/usr/bin/env python +import sys +from OpenGL.GL import * +from OpenGL.GLU import * +from OpenGL import GLX +from OpenGL.raw._GLX import struct__XDisplay +from OpenGL import GL +from ctypes import * + +import Xlib +from Xlib.display import Display +from gi.repository import Gtk, GdkX11, Gdk + + +class gtkgl: + """ these method do not seem to exist in python x11 library lets exploit the c methods """ + xlib = cdll.LoadLibrary('libX11.so') + xlib.XOpenDisplay.argtypes = [c_char_p] + xlib.XOpenDisplay.restype = POINTER(struct__XDisplay) + xdisplay = xlib.XOpenDisplay(None) + display = Xlib.display.Display() + attrs = [] + + xwindow_id = None + width = height = 200 + + def __init__(self): + """ lets setup are opengl settings and create the context for our window """ + self.add_attribute(GLX.GLX_RGBA, True) + self.add_attribute(GLX.GLX_RED_SIZE, 1) + self.add_attribute(GLX.GLX_GREEN_SIZE, 1) + self.add_attribute(GLX.GLX_BLUE_SIZE, 1) + self.add_attribute(GLX.GLX_DOUBLEBUFFER, 0) + + xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes()) + configs = GLX.glXChooseFBConfig(self.xdisplay, 0, None, byref(c_int())) + self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True) + + def add_attribute(self, setting, value): + """just to nicely add opengl parameters""" + self.attrs.append(setting) + self.attrs.append(value) + + def get_attributes(self): + """ return our parameters in the expected structure""" + attrs = self.attrs + [0, 0] + return (c_int * len(attrs))(*attrs) + + def configure(self, wid): + """ """ + self.xwindow_id = GdkX11.X11Window.get_xid(wid) + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ('failed configuring context') + glViewport(0, 0, self.width, self.height) + + def draw_start(self): + """make cairo context current for drawing""" + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ("failed to get the context for drawing") + + def draw_finish(self): + """swap buffer when we have finished drawing""" + GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id) + + def test(self): + """Test method to draw something so we can make sure opengl is working and we can see something""" + self.draw_start() + + glClearColor(0.0, 0.0, 0.0, 0.0) + glClear(GL_COLOR_BUFFER_BIT) + glBegin(GL_TRIANGLES) + glIndexi(0) + glColor3f(1.0, 0.0, 0.0) + glVertex2i(0, 1) + glIndexi(0) + glColor3f(0.0, 1.0, 0.0) + glVertex2i(-1, -1) + glIndexi(0) + glColor3f(0.0, 0.0, 1.0) + glVertex2i(1, -1) + glEnd() + + self.draw_finish() + + +class gui(): + glwrap = gtkgl() + + def __init__(self): + """ create a gtk window, attach a drawing area and connect the relevant events""" + + self.window = Gtk.Window() + self.window.realize() + self.window.resize(self.glwrap.width, self.glwrap.height) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 with opengl") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.drawing_area = Gtk.DrawingArea() + self.drawing_area.connect('configure_event', self.on_configure_event) + self.drawing_area.connect('draw', self.on_draw) + self.drawing_area.set_double_buffered(False) + self.drawing_area.set_size_request(self.glwrap.width, self.glwrap.height) + + self.window.add(self.drawing_area) + self.window.show_all() + + def on_configure_event(self, widget, event): + """if we recieve a configure event for example a resize, then grab the context settings and resize our scene """ + self.glwrap.configure(widget.get_window()) + self.glwrap.test() + return True + + def on_draw(self, widget, context): + """if we recieve a draw event redraw our test triangle""" + self.glwrap.test() + + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/gtk3/13-example-list-box-downloader.org b/bases/do-blog/resources/documents/python/gtk3/13-example-list-box-downloader.org new file mode 100755 index 0000000..178211a --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/13-example-list-box-downloader.org @@ -0,0 +1,191 @@ +#+TITLE: GTK-3 Example downloader using a listbox +#+DATE: 2014-05-01 12:00:00 UTC +#+DESCRIPTION: Populate a listbox with custom widgets, in this case an example file downloader could be the start of a plugin installer. +#+FILETAGS: GTK-3:python:widgets:application:requests +#+CATEGORY: python +#+SLUG: 13-example-list-box-downloader +#+THUMBNAIL: ../../../images/gtk/tut13-listbox.png +#+BEGIN_COMMENT +.. title: GTK-3 Example downloader using a listbox +.. slug: 13-example-list-box-downloader +.. date: 2014-05-01 12:00:00 UTC +.. tags: GTK-3, python, widgets, application, requests +.. category: python +.. description: Populate a listbox with custom widgets, in this case an example file downloader could be the start of a plugin installer. +.. type: text +#+END_COMMENT + +#+CAPTION: File downloader +[[../../../images/gtk/tut13-listbox.png]] + + +A slightly more complex example, loading our gui from a glade file and dynamically add and remove widgets based on an xml file. + +This is the start of a simple file downloader, it reads an xml file and creates a gui dynamically with download buttons for each element in the xml data to retrieve files. + +#+BEGIN_SRC python +#!/usr/bin/env python +import os +import requests +from io import StringIO, BytesIO +import subprocess +from lxml.html import parse +from gi.repository import Gtk, GLib, Gdk, GdkPixbuf + + +class application_gui: + """Tutorial 13 custom treeview list boxes""" + count = 0 + + retrieve_job = None + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut13.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('winFetcher') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['listbox'] = xml.get_object('listbox1') + self.widgets['progress'] = xml.get_object('listProgress') + self.widgets['refresh'] = xml.get_object('btnRefresh') + self.widgets['refresh'].connect('button_press_event', self.refresh) + self.widgets['close'] = xml.get_object('btnClose') + self.widgets['close'].connect('button_press_event', self.closeFetcher) + + #wrap the listbox so we can reuse the code, pass in the listbox widget to our wrapper class + self.listbox = ListBoxSelect(self.widgets['listbox']) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.openFetcher() + self.refresh() + + def openFetcher(self): + self.window.show_all() + + def refresh(self, *args): + """ get a new xml and start the progress bar""" + self.listbox.clear() + self.widgets['progress'].show() + self.retrieve_job = subprocess.Popen( + ['curl', 'file://%s/example.xml' % os.path.abspath('./')], + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + GLib.timeout_add_seconds(1, self.update_active_progress_bar) + + def update_active_progress_bar(self): + """ move the progress bar, when the subprocess returns handle the xml and hide the progress bar""" + self.widgets['progress'].pulse() + if self.retrieve_job.poll(): + return True + self.widgets['progress'].hide() + self.update_list() + return False + + def update_list(self): + """ parse the xmland grab the elements we are intrested in""" + nsmap = {'media': 'http://search.yahoo.com/mrss/'} + results = BytesIO(self.retrieve_job.communicate()[0]) + doc = parse(results).getroot() + for item in doc.iterfind(".//item", namespaces=nsmap): + title = item.find('title').text + link = item.find('link').tail + description = item.find('description').text + image = item.find('thumbnail', namespaces=nsmap).get('url') + self.listbox.model_append(image, title, description, link) + + def download(self): + """ retrieve the xml file in a subprocess using curl """ + self.retrieve_job = subprocess.Popen( + ['curl', 'file://%s/example.xml' % os.path.abspath('./')], + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + def closeFetcher(self, widget): + self.window.hide() + +class ListBoxSelect: + """ handle the listbox rows dynamically add and remove widgets, and handle download. """ + listbox = None + gui_rows = [] # store widgets here so we can destroy them later. + + def __init__(self, listbox): + """ pass in list box to manage and connect event""" + self.listbox = listbox + self.listbox.connect('row-activated', self.selection) + + def selection(self, lbox, lbrow): + """ row selected we may want to react here""" + boxrow = lbrow.get_children()[0] + boxinfo = boxrow.get_children()[1] + print(boxinfo.get_children()[1].get_text()) + + def model_append(self, image, title, description, link): + """ create new widgets, and connect events for our new row""" + items = {} + items['row'] = Gtk.ListBoxRow() + items['vbox'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + items['label1'] = Gtk.Label(title, xalign=0) + items['label2'] = Gtk.Label(link, xalign=0) + items['progress'] = Gtk.ProgressBar() + items['progress'].hide() + items['progress'].set_fraction(0) + items['vbox'].pack_start(items['label1'], True, False, 0) + items['vbox'].pack_start(items['label2'], True, False, 0) + items['vbox'].pack_start(items['progress'], False, False, 0) + + items['hbox'] = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + items['image'] = Gtk.Image.new_from_file(image) + items['button'] = Gtk.Button(label="Download") + items['button'].connect('button_press_event', self.download, items, link) + + items['hbox'].pack_start(items['image'], False, False, 0) + items['hbox'].pack_start(items['vbox'], True, True, 0) + items['button_box'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + + items['button_box'].pack_end(items['button'], False, False, 0) + items['hbox'].pack_start(items['button_box'], False, False, 0) + items['row'].add(items['hbox']) + + self.listbox.add(items['row']) + items['row'].show_all() + + self.gui_rows.append(items) + + def download(self, widget, args, items, link): + """ download button click, change widgets and start the progress bar and download """ + items['button'].hide() + items['job'] = subprocess.Popen( + ['curl', '-O', link], + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + GLib.timeout_add_seconds(1, self.update_active_progress_bar, items) + + def update_active_progress_bar(self, widgets): + """ update progress bar until command finished """ + widgets['progress'].pulse() + if widgets['job'].poll(): + return True + widgets['progress'].hide() + return False + + def clear(self): + """ remove all rows so we can pre-populate""" + for item in self.gui_rows: + item['row'].destroy() + self.gui_rows = [] + +application = application_gui() +Gtk.main() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/gtk3/14-gtk-touchscreen-input.org b/bases/do-blog/resources/documents/python/gtk3/14-gtk-touchscreen-input.org new file mode 100755 index 0000000..5009032 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/14-gtk-touchscreen-input.org @@ -0,0 +1,86 @@ +#+TITLE: GTK-3 Touchscreen events +#+DATE: 2014-05-20 12:00:00 UTC +#+DESCRIPTION: Example of catching touch screen events and filtering device type, helpful for making touch friendly gtk apps. +#+FILETAGS: GTK-3:python:widgets:touchscreen +#+CATEGORY: python +#+SLUG: 14-gtk-touchscreen-input +#+THUMBNAIL: ../../../images/gtk/tut14-touchscreen-events.png +#+BEGIN_COMMENT +.. title: GTK-3 Touchscreen events +.. slug: 14-gtk-touchscreen-input +.. date: 2014-05-20 12:00:00 UTC +.. tags: GTK-3, python, widgets, touchscreen +.. category: python +.. description: Example of catching touch screen events and filtering device type, helpful for making touch friendly gtk apps. +.. type: text +#+END_COMMENT + +#+CAPTION: File downloader +[[../../../images/gtk/tut14-touchscreen-events.png]] + +The example below demonstrates filtering events for a specific device, in this case a touch screen we then capture touch begin update and end events. + +To my knowledge gestures are not supports so you will need to count the begins to determine the number of fingers and handle the updates to determine whats happening. + +#+BEGIN_SRC python +#!/usr/bin/env python +import sys +from gi.repository import Gtk, GdkX11, Gdk + + +class gui(): + + def __init__(self): + self.touch_count = 0 + self.window = Gtk.Window() + self.window.realize() + self.window.resize(300, 300) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 touch events") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.drawing_area = Gtk.DrawingArea() + #add the type of events we are interested in retrieving, skip this step and your events will never fire + self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK) + self.drawing_area.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) + self.drawing_area.connect('button_press_event', self.touched) + self.drawing_area.connect('touch-event', self.touched) + self.drawing_area.set_double_buffered(False) + + self.window.add(self.drawing_area) + self.window.show_all() + + def touched(self, widget, ev): + # detect the type of device we can filter events using this + # if we dont a touch event and mouse event can be triggered from one touch for example + if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHPAD: + print('touchpad device') + if ev.get_source_device().get_source() == Gdk.InputSource.MOUSE: + print('mouse device') + if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN: + print('touchscreen device') + + #from what i can tell there is no support for gestures so you would need another library + #or to handle this yourself + if ev.touch.type == Gdk.EventType.TOUCH_BEGIN: + self.touch_count += 1 + print('start %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y)) + if ev.touch.type == Gdk.EventType.TOUCH_UPDATE: + print('UPDATE %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y)) + if ev.touch.type == Gdk.EventType.TOUCH_END: + self.touch_count -= 1 + print('end %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y)) + if ev.touch.type == Gdk.EventType.TOUCH_CANCEL: + self.touch_count -= 1 + print('cancelled') + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/15-gtk3-opengl-touch-events.org b/bases/do-blog/resources/documents/python/gtk3/15-gtk3-opengl-touch-events.org new file mode 100755 index 0000000..8d3706f --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/15-gtk3-opengl-touch-events.org @@ -0,0 +1,595 @@ +#+TITLE: GTK-3 Simple opengl app with touch events +#+DATE: 2014-06-10 12:00:00 UTC +#+DESCRIPTION: Example application mixing gtk, opengl and touch, written as a demo but also as an opengl testing enviroment. +#+FILETAGS: GTK-3:python:widgets:touchscreen:opengl +#+CATEGORY: python +#+SLUG: 15-gtk3-opengl-touch-events +#+THUMBNAIL: ../../../images/gtk/tut14-touchscreen-events.png +#+BEGIN_COMMENT +.. title: GTK-3 Simple opengl app with touch events +.. slug: 15-gtk3-opengl-touch-events +.. date: 2014-06-10 12:00:00 UTC +.. tags: GTK-3, python, widgets, touchscreen, opengl +.. category: python +.. description: Example application mixing gtk, opengl and touch, written as a demo but also as an opengl testing enviroment. +.. type: text +#+END_COMMENT + + +#+CAPTION: Opengl touch events +[[../../../images/gtk/tut14-touchscreen-events.png]] + + + +This is a much more complicated example which mashes some of the previous examples together, I have written it as an example but also to test out shaders and different pipelines to get better opengl performance. + +Hopefully this example will help you get started writing awesome touch based apps with gtk, if anything is unclear let me know in the comments below. + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/][OpenGL in a drawing area]] + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/][Touch screen events]] + + +#+BEGIN_SRC python :tangle 15-gtk3-opengl-touch-events.py +#!/usr/bin/env python +import sys +import time +import random +import pprint + +import Xlib +from Xlib.display import Display +from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject +from ctypes import * +from numpy import array + +from OpenGL.GL import * +from OpenGL.GL import shaders +from OpenGL.GLU import gluPerspective, gluLookAt +from OpenGL.arrays import vbo +from OpenGL import GLX +try: + from OpenGL.GLX import struct__XDisplay +except ImportError as err: + from OpenGL.raw._GLX import struct__XDisplay +from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER +from OpenGL.GL import shaders, glGetUniformLocation + +from helper import shader +from helper import cube as createcube + +class gtkgl: + """ these method do not seem to exist in python x11 library lets exploit the c methods + useful link http://www.opengl.org/wiki/Programming_OpenGL_in_Linux:_GLX_and_Xlib""" + xlib = cdll.LoadLibrary('libX11.so') + xlib.XOpenDisplay.argtypes = [c_char_p] + xlib.XOpenDisplay.restype = POINTER(struct__XDisplay) + xdisplay = xlib.XOpenDisplay(None) + display = Xlib.display.Display() + attrs = [] + + xwindow_id = None + width = height = 500 + + def __init__(self): + """ lets setup are opengl settings and create the context for our window """ + self.add_attribute(GLX.GLX_RGBA, True) + self.add_attribute(GLX.GLX_RED_SIZE, 8) + self.add_attribute(GLX.GLX_GREEN_SIZE, 8) + self.add_attribute(GLX.GLX_BLUE_SIZE, 8) + self.add_attribute(GLX.GLX_DOUBLEBUFFER, 1) + self.add_attribute(GLX.GLX_DEPTH_SIZE, 24) + + xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes()) + print("run glxinfo to match this visual id %s " % hex(xvinfo.contents.visualid)) + self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True) + + def add_attribute(self, setting, value): + """just to nicely add opengl parameters""" + self.attrs.append(setting) + self.attrs.append(value) + + def get_attributes(self): + """ return our parameters in the expected structure""" + attrs = self.attrs + [0, 0] + return (c_int * len(attrs))(*attrs) + + def configure(self, wid): + """ """ + self.xwindow_id = GdkX11.X11Window.get_xid(wid) + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ('failed configuring context') + glViewport(0, 0, self.width, self.height) + + def draw_start(self): + """make cairo context current for drawing""" + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ("failed to get the context for drawing") + + def draw_finish(self): + """swap buffer when we have finished drawing""" + GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id) + +class scene: + width, height = 600, 600 + + rotationx = 0.0 + rotationy = 0.0 + + rotation_incx = 0.5 + rotation_incy = 0.5 + radius = 0 + + touch_count = 0 + touch_previous = 0, 0 + touch_start_one = 0, 0 + touch_start_two = 0, 0 + touch_end_one = 0, 0 + touch_end_two = 0, 0 + touch_time = 0 + + camera_distance = 25 + + cube_length = 1.0 + cube_size = cube_length / 2 + + def __init__(self): + """setup everything in the correct order""" + self.glwrap = gtkgl() + self.setup_opengl() + self.generate() + self.gui() + + self.mode = 'vbo all cubes' + + def gui(self): + """load in the gui and connect the events and set our properties""" + self.start_time = time.time() + self.frame = 1 + xml = Gtk.Builder() + xml.add_from_file('gui.glade') + + self.window = xml.get_object('window1') + + self.mode_widget = xml.get_object('cmbmode') + self.mode_widget.connect('changed', self.change_mode) + + self.rotate_widget = xml.get_object('spinrotate') + self.rotate_widget.connect('value-changed', self.change_rotate_speed) + + self.radius_widget = xml.get_object('spinradius') + self.radius_widget.connect('value-changed', self.change_radius) + + self.color_widget = xml.get_object('btncolor') + self.color_widget.connect('clicked', self.change_color) + + self.canvas_widget = xml.get_object('canvas') + self.canvas_widget.connect('configure_event', self.on_configure_event) + self.canvas_widget.connect('draw', self.on_draw) + self.canvas_widget.set_double_buffered(False) + self.canvas_widget.set_size_request(self.glwrap.width, self.glwrap.height) + self.canvas_widget.add_events(Gdk.EventMask.TOUCH_MASK) + self.canvas_widget.connect('touch-event', self.touched) + + self.window.show_all() + GObject.idle_add(self.loop_draw) + + def touched(self, widget, ev): + """basic touch support, count the touches so we no how many fingers + basic pinc zoom along the x, single finger slide to rotate""" + if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN: + if ev.touch.type == Gdk.EventType.TOUCH_BEGIN: + self.touch_start = ev.touch.x, ev.touch.y + self.touch_count += 1 + if self.touch_count == 2: + self.touch_start_two = ev.touch.x, ev.touch.y + self.touch_previous = ev.touch.x, ev.touch.y + + if ev.touch.type == Gdk.EventType.TOUCH_UPDATE: + if ev.touch.time - self.touch_time < 100: + return True + + if self.touch_count == 2: + #basic pinch zoom along the x axis + d1 = self.touch_previous[0] - ev.touch.x + if d1 > 1: + self.camera_distance += self.camera_distance * 0.05 + self.touch_previous = ev.touch.x, ev.touch.y + if d1 < 1: + self.camera_distance -= self.camera_distance * 0.05 + self.touch_previous = ev.touch.x, ev.touch.y + self.update_camera() + self.touch_time = ev.touch.time + + if ev.touch.type == Gdk.EventType.TOUCH_END: + self.touch_end = ev.touch.x, ev.touch.y + #set rotation when touch ends + if self.touch_count == 1: + self.rotation_incx = (self.touch_start[0] - self.touch_end[0]) * 0.01 + self.rotation_incy = (self.touch_start[1] - self.touch_end[1]) * 0.01 + self.touch_count = 0 + + def in_circle(self, center_x, center_y, center_z, radius, x, y, z): + """ test if our cordinate lies inside our sphere""" + square_dist = (center_x - x) ** 2 + (center_y - y) ** 2 + (center_z - z) ** 2 + return square_dist <= radius ** 2 + + def change_color(self, widget): + #regenerate the scene + self.generate() + + def change_mode(self, widget): + #change whats drawn and how + self.mode = widget.get_active_text().lower() + print(widget.get_active_text()) + + def change_rotate_speed(self, widget): + #handle spinner rotation speed event + self.rotation_incx = widget.get_value() + self.rotation_incy = widget.get_value() + + def change_radius(self, widget): + #increase size of circle and number of polygons + self.radius = int(widget.get_value()) + self.generate() + + def loop_draw(self): + #send redraw event to drawing area widget + self.canvas_widget.queue_draw() + return True + + def on_configure_event(self, widget, event): + """if we recieve a configure event for example a resize, then grab the context settings and resize our scene """ + self.glwrap.width = widget.get_allocation().width + self.glwrap.height = widget.get_allocation().height + self.width, self.height = self.glwrap.width, self.glwrap.height + + #update our states because we have reconfigured the display + self.glwrap.configure(widget.get_window()) + self.glwrap.draw_start() + self.update_camera() + self.setup_shaders() + + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDepthFunc(GL_LEQUAL) + glDepthRange(0.0, 1.0) + glEnable(GL_CULL_FACE) + glCullFace(GL_BACK) + glFrontFace(GL_CW) + + self.glwrap.draw_finish() + return True + + def on_draw(self, widget, context): + """if we recieve a draw event redraw our opengl scene""" + self.elapsed_time = time.time() - self.start_time + self.frame += 1 + + if self.elapsed_time > 1: + print('fps %d' % self.frame) + self.start_time = time.time() + self.frame = 1 + self.glwrap.draw_start() + self.draw() + self.glwrap.draw_finish() + + def generate(self): + self.cubes = [] + #position cubes inside a sphere radius + for shift_x in range(-self.radius, self.radius + 1): + for shift_y in range(-self.radius, self.radius + 1): + for shift_z in range(-self.radius, self.radius + 1): + x = shift_x * self.cube_length + y = shift_y * self.cube_length + z = shift_z * self.cube_length + if not self.in_circle(0, 0, 0, self.radius, x, y, z): + continue + #random colours / textures if we want + color = random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15]) + self.cubes.append(createcube((x, y, z), color, self.cube_size)) + + self.test_cube = createcube((x, y, z), (random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15])), 6) + faces = [] + for cube in self.cubes: + faces += cube.get_data() + + print('Generated %s Cubes' % str(len(self.cubes))) + print('Generated %s Tringles' % str(len(faces) / 3)) + self.vbuffer = vbo.VBO(array(faces, 'f')) + + def setup_shaders(self): + self.shader_program = shader() + self.shader_program.compile() + + def setup_opengl(self): + glShadeModel(GL_SMOOTH) + glClearColor(0.0, 0.0, 0.0, 0.0) + glClearDepth(1.0) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) + glPolygonMode(GL_FRONT, GL_FILL) + + def update_camera(self): + glViewport(0, 0, self.width, self.height) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluPerspective(45, 1.0 * self.width / self.height, 1.0, 80.0) + gluLookAt(self.camera_distance, self.camera_distance, self.camera_distance, # location + 0.0, 0.0, 0.0, # lookat + 0.0, 1.0, 0.0) # up direction + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + + def draw_test(self): + #lets do a simple rotation so we can see the objects are 3d + glRotatef(self.rotationx, 1.0, 0.0, 0.0) + self.rotationx += self.rotation_incx + glRotatef(self.rotationy, 0.0, 1.0, 0.0) + self.rotationy += self.rotation_incy + + #use our shader program and enable vertex loading + glUseProgram(self.shader_program.program) + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_COLOR_ARRAY) + + #render the triangles into a virtual buffer object + self.test_cube.bind() + glVertexPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer) + glColorPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer + 12) + glDrawArrays(GL_TRIANGLES, 0, self.test_cube.vbuffer_size) + self.test_cube.unbind() + + #restore opengl to our previous state + glDisableClientState(GL_COLOR_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + shaders.glUseProgram(0) + + def draw_vbo_per_cube(self): + #lets do a simple rotation so we can see the objects are 3d + glRotatef(self.rotationx, 1.0, 0.0, 0.0) + self.rotationx += self.rotation_incx + glRotatef(self.rotationy, 0.0, 1.0, 0.0) + self.rotationy += self.rotation_incy + + # use our shader program and enable vertex loading + glUseProgram(self.shader_program.program) + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_COLOR_ARRAY) + + # render the triangles into a virtual buffer object + for shape in self.cubes: + shape.bind() + glVertexPointer(3, GL_FLOAT, 24, shape.vbuffer) + glColorPointer(3, GL_FLOAT, 24, shape.vbuffer + 12) + glDrawArrays(GL_TRIANGLES, 0, shape.vbuffer_size) + shape.unbind() + + #restore opengl to our previous state + glDisableClientState(GL_COLOR_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + shaders.glUseProgram(0) + + def draw_vbo_all_cubes(self): + #lets do a simple rotation so we can see the objects are 3d + glRotatef(self.rotationx, 1.0, 0.0, 0.0) + self.rotationx += self.rotation_incx + glRotatef(self.rotationy, 0.0, 1.0, 0.0) + self.rotationy += self.rotation_incy + + # use our shader program and enable vertex loading + glUseProgram(self.shader_program.program) + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_COLOR_ARRAY) + + # render the triangles into a virtual buffer object + self.vbuffer.bind() + glVertexPointer(3, GL_FLOAT, 24, self.vbuffer) + glColorPointer(3, GL_FLOAT, 24, self.vbuffer + 12) + glDrawArrays(GL_TRIANGLES, 0, len(self.vbuffer)) + self.vbuffer.unbind() + + #restore opengl to our previous state + glDisableClientState(GL_COLOR_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + shaders.glUseProgram(0) + + def draw(self): + glEnable(GL_DEPTH_TEST) + glClearDepth(1.0) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + if self.mode == 'vbo test cube': + self.draw_test() + if self.mode == 'vbo per cube': + self.draw_vbo_per_cube() + if self.mode == 'vbo all cubes': + self.draw_vbo_all_cubes() + + +if __name__ == '__main__': + glexample = scene() + GLib.threads_init() + Gdk.threads_init() + Gdk.threads_enter() + Gtk.main() + Gdk.threads_leave() +#+END_SRC + +The file below is a simple helper to reduce the size of the files, it contains the shader and a point and cube class used in the main program above. + +#+BEGIN_SRC python :tangle helper.py +import numpy +from numpy import array +from OpenGL.GL import * +from OpenGL.GL import shaders +from OpenGL.arrays import vbo + +from OpenGL._bytes import bytes, _NULL_8_BYTE + +def compileShader( source, shaderType ): + + """Compile shader source of given type + source -- GLSL source-code for the shader + shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc, + returns GLuint compiled shader reference + raises RuntimeError when a compilation failure occurs + """ + if isinstance(source, str): + + source = [source] + elif isinstance(source, bytes): + + source = [source.decode('utf-8')] + + shader = glCreateShader(shaderType) + glShaderSource(shader, source) + glCompileShader(shader) + result = glGetShaderiv(shader, GL_COMPILE_STATUS) + + if not(result): + # TODO: this will be wrong if the user has + # disabled traditional unpacking array support. + raise RuntimeError( + """Shader compile failure (%s): %s"""%( + result, + glGetShaderInfoLog( shader ), + ), + source, + shaderType, + ) + return shader + + +class shader: + vertex = """#version 120 + //attributes in values + attribute vec3 inPosition; + attribute vec3 inColor; + + //varying sending to fragment shader + varying vec4 outColor; + void main(){ + vec4 fragmentPos = gl_ModelViewMatrix * gl_Vertex; + gl_Position = (gl_ProjectionMatrix * fragmentPos); + outColor = vec4(gl_Color.rgb, 1); + }""" + + fragment = """#version 120 + varying vec4 outColor; + void main(){ + gl_FragColor = outColor; + }""" + + program = None + def compile(self): + self.program = shaders.compileProgram( + compileShader(self.vertex, GL_VERTEX_SHADER), + compileShader(self.fragment, GL_FRAGMENT_SHADER),) + +class point: + __slots__ = ['x', 'y', 'z', 'xyz', 'vertex'] + + def __init__(self, p, c=(1, 0, 0)): + """ Position in 3d space as a tuple or list, and colour in tuple or list format""" + self.x, self.y, self.z = p + self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f') + + +class cube: + def __init__(self, p1, color, size=0.5): + self.color = array([1, 0, 0], 'f') + #self.xyz = p1 + self.points = ( + point((p1[0] - size, p1[1] + size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] - size), (color)), + + point((p1[0] - size, p1[1] - size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] - size), (color)), + + ) + + self.vbuffer = vbo.VBO(array(self.get_data(), 'f')) + self.vbuffer_size = len(self.get_data()) + + def bind(self): + self.vbuffer.bind() + + def unbind(self): + self.vbuffer.unbind() + + def get_data(self): + return ( + self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, + self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, + + self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, + self.points[0].vertex, self.points[5].vertex, self.points[4].vertex, + + self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, + self.points[0].vertex, self.points[4].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, + self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[1].vertex, self.points[2].vertex, + self.points[6].vertex, self.points[5].vertex, self.points[1].vertex, + + self.points[6].vertex, self.points[4].vertex, self.points[5].vertex, + self.points[6].vertex, self.points[7].vertex, self.points[4].vertex, + ) + + + +class cube: + def __init__(self, p1, color, size=0.5): + self.color = array([1, 0, 0], 'f') + #self.xyz = p1 + self.points = ( + point((p1[0] - size, p1[1] + size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] - size), (color)), + + point((p1[0] - size, p1[1] - size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] - size), (color)), + + ) + + self.vbuffer = vbo.VBO(array(self.get_data(), 'f')) + self.vbuffer_size = len(self.get_data()) + + def bind(self): + self.vbuffer.bind() + + def unbind(self): + self.vbuffer.unbind() + + def get_data(self): + return ( + self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, + self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, + + self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, + self.points[0].vertex, self.points[5].vertex, self.points[4].vertex, + + self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, + self.points[0].vertex, self.points[4].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, + self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[1].vertex, self.points[2].vertex, + self.points[6].vertex, self.points[5].vertex, self.points[1].vertex, + + self.points[6].vertex, self.points[4].vertex, self.points[5].vertex, + self.points[6].vertex, self.points[7].vertex, self.points[4].vertex, + ) + +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/gtk3/16-gtk3-custom-signals-example.org b/bases/do-blog/resources/documents/python/gtk3/16-gtk3-custom-signals-example.org new file mode 100755 index 0000000..e9e8dab --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/16-gtk3-custom-signals-example.org @@ -0,0 +1,209 @@ +#+TITLE: GTK-3 custom signals example +#+DATE: 2014-09-11 12:00:00 UTC +#+DESCRIPTION: Simple example on how to setup, connect to and trigger signals. +#+FILETAGS: GTK-3:python:widgets:signals +#+CATEGORY: python +#+SLUG: 16-gtk3-custom-signals-example +#+THUMBNAIL: ../../../images/gtk/tut16-signals.png +#+BEGIN_COMMENT +.. title: GTK-3 custom signals example +.. slug: 16-gtk3-custom-signals-example +.. date: 2014-09-11 12:00:00 UTC +.. tags: GTK-3, python, widgets, signals +.. category: python +.. description: Simple example on how to setup, connect to and trigger signals. +.. type: text +#+END_COMMENT + + +#+CAPTION: Opengl touch events +[[../../../images/gtk/tut16-signals.png]] + + +In this program we create 4 custom signals, the first to are connected to and triggered on launch, the latter two are triggered when you click the buttons. + +We can create new signals using GObject.signal_new, we can then connect the new signal and callback as we normally would using widget .connect methods as normal. +To trigger our custom event we use the .emit() method, this method also aalows us to fake events, for example we might want to fake the user clicking a button. + + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/][Gtk signals]] + + +View python code +#+BEGIN_SRC python :tangle 16-gtk3-custom-signals-example.py +#!/usr/bin/python +from gi.repository import Gtk +from gi.repository import GObject + +class application_gui: + """Tutorial 02 buttons""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut16-signals.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + GObject.signal_new('custom-signal1', self.window, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + GObject.signal_new('custom-signal2', self.window, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + + self.window.connect('custom-signal1', self.custom_signal1_method) + self.window.connect('custom-signal2', self.custom_signal2_method) + + #emit the custom signals on launch. + self.window.emit('custom-signal1', 'hello from signal1') + self.window.emit('custom-signal2', None) + + self.buttons = {} + self.buttons['but1'] = xml.get_object('button1') + self.buttons['but2'] = xml.get_object('button2') + self.buttons['but3'] = xml.get_object('togglebutton1') + self.buttons['but4'] = xml.get_object('togglebutton2') + + GObject.signal_new('custom-signal3', self.buttons['but1'], GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + GObject.signal_new('custom-signal4', self.buttons['but2'], GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + + self.buttons['but1'].connect('clicked', self.button_events) + self.buttons['but1'].connect('custom-signal3', self.custom_signal3_method) + self.buttons['but2'].connect('clicked', self.button_events) + self.buttons['but2'].connect('custom-signal4', self.custom_signal4_method) + self.buttons['but3'].connect('clicked', self.button_events) + self.buttons['but4'].connect('clicked', self.button_events) + + #fake the user clicking one of our buttons + self.buttons['but1'].emit('clicked') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def custom_signal1_method(self, *args): + print('custom_signal1 emitted') + print(args) + + def custom_signal2_method(self, *args): + print('custom_signal2 emitted') + print(args) + + def custom_signal3_method(self, *args): + print('custom_signal3 emitted') + print(args) + + def custom_signal4_method(self, *args): + print('custom_signal4 emitted') + print(args) + + def button_events(self, widget): + widget.emit('custom-signal3', ('1', '2', '3')) + toggle_value = '' + if widget.get_name() == 'GtkToggleButton': + toggle_value = str(widget.get_active()) + self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value) + +application = application_gui() +Gtk.main() +#+END_SRC + +View glade XML +#+BEGIN_SRC xml :tangle tut16-signals.glade + + + + + False + + + True + False + vertical + + + True + False + + + Button One Standard Button + True + False + True + + + 0 + 0 + 1 + 1 + + + + + Button Three Toggle Button + True + False + True + + + 1 + 0 + 1 + 1 + + + + + Button Four Toggle Button + True + False + True + + + 1 + 1 + 1 + 1 + + + + + Button Two Standard Button + True + False + True + + + 0 + 1 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/gtk3/__init__.py b/bases/do-blog/resources/documents/python/gtk3/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/bases/do-blog/resources/documents/python/gtk3/geany.history b/bases/do-blog/resources/documents/python/gtk3/geany.history new file mode 100755 index 0000000..f54bde5 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/geany.history @@ -0,0 +1 @@ +/home/omarks/Ubuntu One/python/gtk3/tut04-text-dropdown-spin.py diff --git a/bases/do-blog/resources/documents/python/gtk3/gtk-printing.org b/bases/do-blog/resources/documents/python/gtk3/gtk-printing.org new file mode 100755 index 0000000..84cbd45 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/gtk-printing.org @@ -0,0 +1,137 @@ +#+TITLE: Printing with Cairo GTK3 and python +#+DATE: 2015-12-05 12:00:00 UTC +#+DESCRIPTION: Example demonstrating how to draw a graphic and position it on a page for printing complete with preview and printer settings dialog. +#+FILETAGS: GTK-3:python:widgets +#+CATEGORY: python +#+SLUG: printing-with-cairo-gtk3-python +#+BEGIN_COMMENT +.. title: Printing with Cairo GTK3 and python +.. slug: printing-with-cairo-gtk3-python +.. date: 2015-12-05 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Example demonstrating how to draw a graphic and position it on a page for printing complete with preview and printer settings dialog. +.. type: text +#+END_COMMENT + + +#+CAPTION: You can download image used in the example below. +[[../../../images/gtk/gtk_print_example_01.png]] + +#+CAPTION: You can download image used in the example below. +[[../../../images/misc/testing-256px.png]] + + +This example uses the gtk3 toolkit the same toolkit used by popular software like gimp and inkscape, it should be quite easy to convert to gtk2 if required the advantage of using gtk is that the libraries are cross platform so you should be +able to print on all platform however i have only tested this on ubuntu 15.04 so far. + + +#+BEGIN_SRC python +#!/usr/bin/python +# -*- coding: utf8 -*- +from gi.repository import Gtk, Pango, PangoCairo#, cairo +import cairo + +# a4 = 210 x 297 +page_width = 210 +page_height = 297 +page_margin_top = 20 +dpi = 600 +_mm_dpi = 72 / 25.4 + +class PrintExample(Gtk.Window): + + def __init__(self): + self.print_image() + + def print_image(self): + """setup the paper size, print settings and kick of the print operation""" + + ps = Gtk.PaperSize.new_custom("cc", "cc", page_width, page_height, Gtk.Unit.MM) + print_settings = Gtk.PrintSettings() + print_settings.set_resolution(dpi) + + page_setup = Gtk.PageSetup() + page_setup.set_paper_size(ps) + page_setup.set_bottom_margin(10.0, Gtk.Unit.MM) + page_setup.set_left_margin(5.0, Gtk.Unit.MM) + page_setup.set_right_margin(4.5, Gtk.Unit.MM) + page_setup.set_top_margin(10.0, Gtk.Unit.MM) + page_setup.set_orientation(Gtk.PageOrientation.PORTRAIT) + + + print_operation = Gtk.PrintOperation() + print_operation.set_n_pages(1) + print_operation.set_default_page_setup(page_setup) + print_operation.set_print_settings(print_settings) + print_operation.connect("draw_page", self.draw_page) + print_operation.set_export_filename("example.pdf") + + result = print_operation.run(Gtk.PrintOperationAction.PREVIEW, None) + print(result) + + + def draw_page (self, operation, print_context, page_number): + """draw with cairo to the print context layout for printing on sticky labels on an a4 sheet""" + cr = print_context.get_cairo_context() + cr.set_source_rgb(0, 0, 0) + + ims=cairo.ImageSurface.create_from_png("test.png") + cr.set_line_width(1) + + desc = Pango.FontDescription("sans 5") + + layou_title_text = PangoCairo.create_layout(cr) + layou_title_text.set_text("Title Text", -1) + layou_title_text.set_font_description(desc) + layou_title_text.set_width(100 * Pango.SCALE) + layou_title_text.set_wrap(Pango.WrapMode.WORD) + + layou_desc_text = PangoCairo.create_layout(cr) + layou_desc_text.set_text("Lorem ipsum .....", -1) + layou_desc_text.set_font_description(desc) + layou_desc_text.set_width(50 * Pango.SCALE) + layou_desc_text.set_wrap(Pango.WrapMode.CHAR) + + #offset is the distance from the start of one box to the next including blank space + rectangle_x_offset = 40.5 * _mm_dpi + rectangle_y_offset = 21.5 * _mm_dpi + + #actual size of the box + rectangle_width = 38 * _mm_dpi + rectangle_height = 21.5 * _mm_dpi + columns = int(page_width / 38) + rows = int((page_height-page_margin_top) / 21) + + for column in range(0, columns): + for row in range(0, rows): + #qr code + cr.set_line_width(0.1) + cr.save() + cr.translate(column * rectangle_x_offset, row * rectangle_y_offset + 15) + cr.scale(0.15, 0.15) + cr.set_source_surface(ims, 0, 0) + cr.paint() + cr.restore() + + #rectangle + cr.rectangle(column * rectangle_x_offset , row * rectangle_y_offset, rectangle_width , rectangle_height ) + cr.stroke() + + #title text + cr.save() + cr.move_to(column * rectangle_x_offset + 4, row * rectangle_y_offset + 4) + PangoCairo.update_layout(cr, layou_title_text) + PangoCairo.show_layout (cr, layou_title_text) + cr.restore() + + #description text + cr.save() + cr.move_to(column * rectangle_x_offset + 50, row * rectangle_y_offset + 14) + PangoCairo.update_layout(cr, layou_desc_text) + PangoCairo.show_layout (cr, layou_desc_text) + cr.restore() + +win = PrintExample() +Gtk.main() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/gtk3/images/interactive-python-textview.png b/bases/do-blog/resources/documents/python/gtk3/images/interactive-python-textview.png new file mode 100755 index 0000000..c478a9b Binary files /dev/null and b/bases/do-blog/resources/documents/python/gtk3/images/interactive-python-textview.png differ diff --git a/bases/do-blog/resources/documents/python/gtk3/index.py b/bases/do-blog/resources/documents/python/gtk3/index.py new file mode 100755 index 0000000..e22e93f --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/index.py @@ -0,0 +1,67 @@ +#import required files +#append this to module path so we can load from the parent directory +import sys, os +from scaffold.web import web as html +from scaffold.loaders import load_partial + +web = html() + + +class page: + name = __name__ + title = 'Python GTK examples.' + intro = 'Various examples of using gtk widgets in python to build applications.' + date = '2013-01-01' + path = os.path.abspath('./') + tags = 'python', 'gtk3', 'index' + + def create_content(self): + web.page.section(web.list.render()) + + web.page.section( + web.paragraph.create( + """Index of various code snippets demonstrating using the different gtk3 widgets, they should all run in both python 2 and 3. + I plan to update and add to these examples, if you would like to see any specific examples let me know.""" + ).render() + ) + + web.pre.create('bzr branch lp:~oly/python-examples/trunk').render() + web.link.create('Launchpad','View Code here','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/') + + web.page.section( + web.paragraph.create( + """You can checkout all examples and the glade files to run by using the commad below in a linke terminal or view it directly on """ + ).append( + web.link.render() + ).append( + web.pre.render() + ).render() + ) + + pages = ( + ('GTK3 Display a window'), + ('GTK3 Buttons and switches'), + ('GTK3 Radio Buttons and switches'), + ('GTK3 Dropdowns and spin buttons'), + ('GTK3 progress bar and spinner'), + ('GTK3 Dialog boxes'), + ('GTK3 App Chooser and scale buttons'), + ('GTK3 Calendars and menus'), + ('GTK3 Treeview in liststore mode'), + ('GTK3 Treeview in treestore mode'), + ('GTK3 Textview interaction'), + ('GTK3 Displaying OpenGL inside a drawing area'), + ('GTK3 Listbox downloader'), + ('GTK3 Touch screen events'), + ('GTK3 Simple Opengl application with gtk and Touch screen events'), + ('GTK3 Creating and connecting to custom signals'), + ) + + count = 1 + web.list.create('') + for item in pages: + web.link.create(item, item ,'./tutorial' + str(count) + '.htm') + web.list.append(web.link.render()) + count += 1 + web.page.section(web.list.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut01/tut01-windows.py b/bases/do-blog/resources/documents/python/gtk3/tut01/tut01-windows.py new file mode 100755 index 0000000..56731c2 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut01/tut01-windows.py @@ -0,0 +1,24 @@ +#!/usr/bin/python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 01 Create and destroy a window""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut01.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut01/tut01.glade b/bases/do-blog/resources/documents/python/gtk3/tut01/tut01.glade new file mode 100755 index 0000000..e1c906e --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut01/tut01.glade @@ -0,0 +1,10 @@ + + + + + False + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut02/tut02-buttons.glade b/bases/do-blog/resources/documents/python/gtk3/tut02/tut02-buttons.glade new file mode 100755 index 0000000..4321c4d --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut02/tut02-buttons.glade @@ -0,0 +1,96 @@ + + + + + False + + + True + False + vertical + + + True + False + + + Button One Standard Button + True + False + True + + + 0 + 0 + 1 + 1 + + + + + Button Three Toggle Button + True + False + True + 0.49000000953674316 + 0.49000000953674316 + + + 1 + 0 + 1 + 1 + + + + + Button Four Toggle Button + True + False + True + + + 1 + 1 + 1 + 1 + + + + + Button Two Standard Button + True + False + True + + + 0 + 1 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut02/tut02-buttons.py b/bases/do-blog/resources/documents/python/gtk3/tut02/tut02-buttons.py new file mode 100755 index 0000000..6a89c31 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut02/tut02-buttons.py @@ -0,0 +1,43 @@ +#!/usr/bin/python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 02 buttons""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut02-buttons.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + self.buttons = {} + self.buttons['but1'] = xml.get_object('button1') + self.buttons['but2'] = xml.get_object('button2') + self.buttons['but3'] = xml.get_object('togglebutton1') + self.buttons['but4'] = xml.get_object('togglebutton2') + + self.buttons['but1'].connect('clicked', self.button_events) + self.buttons['but2'].connect('clicked', self.button_events) + self.buttons['but3'].connect('clicked', self.button_events) + self.buttons['but4'].connect('clicked', self.button_events) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def button_events(self, widget): + toggle_value = '' + if widget.get_name() == 'GtkToggleButton': + toggle_value = str(widget.get_active()) + self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value) + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut03/tut03-switch-radio.py b/bases/do-blog/resources/documents/python/gtk3/tut03/tut03-switch-radio.py new file mode 100755 index 0000000..357498b --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut03/tut03-switch-radio.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 03 Radio Buttons and switches""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut03.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + self.buttons = {} + self.buttons['radio1'] = xml.get_object('radiobutton1') + self.buttons['radio2'] = xml.get_object('radiobutton2') + self.buttons['switch1'] = xml.get_object('switch1') + self.buttons['switch2'] = xml.get_object('switch2') + + #set a name of the switches because they dont have a label so we can use this to show selected button + self.buttons['switch1'].set_name('switch1') + self.buttons['switch2'].set_name('switch2') + + #connect the interface events to functions + self.buttons['switch1'].connect('button-press-event', self.switch_button_events) + self.buttons['switch2'].connect('button-press-event', self.switch_button_events) + self.buttons['radio1'].connect('toggled', self.radio_button_events) + self.buttons['radio2'].connect('toggled', self.radio_button_events) + + #add the second radio to the first radio button making it part of the group + self.buttons['radio2'].join_group(self.buttons['radio1']) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def switch_button_events(self, widget, params): + toggle_value = str(widget.get_active()) + self.text.set_text(widget.get_name() + ' ' + toggle_value) + + def radio_button_events(self, widget): + toggle_value = str(widget.get_active()) + self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value) + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut03/tut03.glade b/bases/do-blog/resources/documents/python/gtk3/tut03/tut03.glade new file mode 100755 index 0000000..beb226e --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut03/tut03.glade @@ -0,0 +1,99 @@ + + + + + False + + + True + False + vertical + + + True + False + + + radiobutton one + True + False + False + half + 0.0099999997764825821 + 0.50999999046325684 + True + True + + + 0 + 0 + 1 + 1 + + + + + radiobutton two + True + False + False + 0 + True + True + + + 0 + 1 + 1 + 1 + + + + + True + False + + + 1 + 0 + 1 + 1 + + + + + True + False + True + + + 1 + 1 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut04/tut04-text-dropdown-spin.py b/bases/do-blog/resources/documents/python/gtk3/tut04/tut04-text-dropdown-spin.py new file mode 100755 index 0000000..22e66b9 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut04/tut04-text-dropdown-spin.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +from gi.repository import Gtk + + +class application_gui: + """Tutorial 04 text input, spin input, drop down options""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut04.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['spin1'] = xml.get_object('spinbutton1') + self.widgets['spin2'] = xml.get_object('spinbutton2') + + #simple dropdown just text + self.widgets['comboboxtext'] = xml.get_object('comboboxtext1') + + #more complex multi row multi data types in dropdown using renderers + self.widgets['combobox'] = xml.get_object('combobox1') + self.widgets['combobox_liststore']= xml.get_object('liststore1') + #self.widgets['combobox_cell_renderer']= xml.get_object('liststore1') + + #add some items to the drop down + self.widgets['combobox_liststore'].append(['new row test']) + self.widgets['combobox_liststore'].append(['second row test']) + + #adding new rows to the dropdown + self.widgets['comboboxtext'].append_text('row4') + self.widgets['comboboxtext'].append_text('row5') + + #connect to events + self.widgets['comboboxtext'].connect('changed', self.dropdown_event_text) + self.widgets['spin1'].connect('value-changed', self.spin_button_event) + self.widgets['spin2'].connect('value-changed', self.spin_button_event) + self.widgets['combobox'].connect('changed', self.dropdown_event) + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show_all() + + def dropdown_event_text(self, widget): + self.text.set_text(widget.get_active_text()) + + def dropdown_event(self, widget): + list_view_model = widget.get_model() + active_iter_index = widget.get_active() + row_iter = list_view_model.get_iter(active_iter_index) + self.text.set_text(widget.get_name() + ' ' +list_view_model.get_value(row_iter, 0 )) + + def spin_button_event(self, widget): + self.text.set_text(widget.get_name() + ' ' + ' ' + str(widget.get_value())) + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut04/tut04.glade b/bases/do-blog/resources/documents/python/gtk3/tut04/tut04.glade new file mode 100755 index 0000000..0192993 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut04/tut04.glade @@ -0,0 +1,129 @@ + + + + + + 100 + 10 + 10 + + + 10 + 0.10000000000000001 + 10 + + + + + + + + + row1 + + + row2 + + + row3 + + + + + False + + + True + False + vertical + + + True + False + + + True + True + adjustment2 + 1 + + + 0 + 0 + 1 + 1 + + + + + True + True + adjustment1 + 0.01 + + + 0 + 1 + 1 + 1 + + + + + True + False + liststore1 + 1 + + + + 0 + + + + + 1 + 0 + 1 + 1 + + + + + True + False + + row1 + row2 + row3 + + + + 1 + 1 + 1 + 1 + + + + + False + True + 0 + + + + + True + True + + + False + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut05/tut05-progress-spinners.py b/bases/do-blog/resources/documents/python/gtk3/tut05/tut05-progress-spinners.py new file mode 100755 index 0000000..86ded99 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut05/tut05-progress-spinners.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 progress and spinner widgets] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 Spinning graphic and progress bars] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gtk, GLib + + +class application_gui: + """Tutorial 05 demo progress bars and the spinner graphic.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut05.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['entry'] = xml.get_object('entry2') + + self.widgets['spinner1'] = xml.get_object('spinner1') + self.widgets['spinnerbutton'] = xml.get_object('togglebutton1') + self.widgets['progress1'] = xml.get_object('progressbar1') + self.widgets['progress2'] = xml.get_object('progressbar2') + + self.widgets['spinnerbutton'].connect('clicked', self.toggle_spinner) + + self.widgets['progress1'].set_text('active mode') + self.widgets['progress2'].set_text('percentage mode') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + self.widgets['spinner1'].start() + + GLib.timeout_add_seconds(1, self.update_active_progress_bar) + GLib.timeout_add_seconds(1, self.update_percentage_progress_bar) + + def update_active_progress_bar(self): + # nudge the progress bar to show something is still happening, just updating every second in the example + # long running processes which have an unknow finish time would use this version + self.widgets['progress1'].pulse() + return True + + def update_percentage_progress_bar(self): + self.count +=0.1 + self.widgets['progress2'].set_fraction(self.count) + if self.count<1: + return True + else: + return False + + def toggle_spinner(self, widget): + if widget.get_active(): + self.widgets['spinner1'].start() + else: + self.widgets['spinner1'].stop() + + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut05/tut05.glade b/bases/do-blog/resources/documents/python/gtk3/tut05/tut05.glade new file mode 100755 index 0000000..83ba3ce --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut05/tut05.glade @@ -0,0 +1,92 @@ + + + + + + False + + + True + False + vertical + + + True + False + + + Toggle Spinner + True + False + True + True + + + 1 + 1 + 1 + 1 + + + + + True + False + + + 1 + 0 + 1 + 1 + + + + + True + False + True + + + 0 + 1 + 1 + 1 + + + + + True + False + True + + + 0 + 0 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut06/tut06-dialogs.py b/bases/do-blog/resources/documents/python/gtk3/tut06/tut06-dialogs.py new file mode 100755 index 0000000..ab9da19 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut06/tut06-dialogs.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 dialogs] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 font colour and file dialogs] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gtk + + +class application_gui: + """Tutorial 06 demo of file, colour and font dialogs.""" + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut06.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['entry'] = xml.get_object('entry2') + self.widgets['color_button'] = xml.get_object('btnColour') + self.widgets['font_button'] = xml.get_object('btnFont') + self.widgets['open_button'] = xml.get_object('btnLoad') + self.widgets['save_button'] = xml.get_object('btnSave') + + self.widgets['color_button'].connect('clicked', self.colour_selector) + self.widgets['font_button'].connect('clicked', self.font_selector) + self.widgets['save_button'].connect('clicked', self.save_file_selector) + self.widgets['open_button'].connect('clicked', self.open_file_selector) + + self.widgets['open_dialog'] = xml.get_object('fileChooserLoad') + self.widgets['open_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE) + self.widgets['open_dialog'].add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK) + + self.widgets['save_dialog'] = xml.get_object('fileChooserSave') + self.widgets['save_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE) + self.widgets['save_dialog'].add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.OK) + + self.widgets['color_dialog'] = xml.get_object('colorSelect') + + self.widgets['font_dialog'] = xml.get_object('fontSelect') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def colour_selector(self, widget): + self.widgets['color_dialog'].run() + self.colour = self.widgets['color_dialog'].get_color_selection().get_current_color() + + #accessing the individual values as integers, converting to floats here + colour = [self.colour.red / 65535.0, + self.colour.green / 65535.0, + self.colour.blue / 65535.0] + + self.widgets['color_dialog'].hide() + self.text.set_text(self.widgets['color_dialog'].get_name() + ' ' + self.colour.to_string()) + + def font_selector(self, widget): + self.widgets['font_dialog'].run() + self.font = self.widgets['font_dialog'].get_font_selection() + self.text.set_text(self.widgets['font_dialog'].get_name() + ' ' + self.font.get_font_name() + ' ' + str(self.font.get_size())) + self.widgets['font_dialog'].hide() + + def open_file_selector(self, widget): + response = self.widgets['open_dialog'].run() + if self.widgets['open_dialog'].get_filenames(): + self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ str(self.widgets['open_dialog'].get_filenames())) + else: + if self.widgets['open_dialog'].get_filename(): + self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ self.widgets['open_dialog'].get_filename()) + self.widgets['open_dialog'].hide() + + def save_file_selector(self, widget): + response = self.widgets['save_dialog'].run() + self.text.set_text(self.widgets['save_dialog'].get_name() + ' '+ self.widgets['save_dialog'].get_filename()) + self.widgets['save_dialog'].hide() + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut06/tut06.glade b/bases/do-blog/resources/documents/python/gtk3/tut06/tut06.glade new file mode 100755 index 0000000..2bc5712 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut06/tut06.glade @@ -0,0 +1,122 @@ + + + + + False + 5 + dialog + + + + False + 5 + GtkFileChooserDialog + dialog + + + False + 5 + GtkFileChooserDialog + dialog + + + False + 5 + dialog + + + False + + + True + False + vertical + + + True + False + + + gtk-open + True + False + True + True + + + 0 + 0 + 1 + 1 + + + + + gtk-save + True + False + True + True + right + + + 1 + 0 + 1 + 1 + + + + + gtk-select-color + True + False + True + True + + + 0 + 1 + 1 + 1 + + + + + gtk-select-font + True + False + True + True + + + 1 + 1 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut07/tut07-appchooser-scale.py b/bases/do-blog/resources/documents/python/gtk3/tut07/tut07-appchooser-scale.py new file mode 100755 index 0000000..4285109 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut07/tut07-appchooser-scale.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 app chooser and scale widget] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 scale widget and app chooser example] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gtk, GLib, Gio + + +class application_gui: + """Tutorial 04 text input, spin input, drop down options""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut07.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['scale1'] = xml.get_object('scalebutton1') + self.widgets['scale2'] = xml.get_object('scalebutton2') + self.widgets['appchooseraudio'] = xml.get_object('appchooserbutton1') + self.widgets['appchoosertext'] = xml.get_object('appchooserbutton2') + + self.widgets['appchooseraudio'].connect('changed', self.app_chooser) + self.widgets['appchoosertext'].connect('changed', self.app_chooser) + self.widgets['scale1'].connect('value-changed', self.scale) + self.widgets['scale2'].connect('value-changed', self.scale) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + def app_chooser(self, widget): + list_view_model = widget.get_model() + active_iter_index = widget.get_active() + row_iter = list_view_model.get_iter(active_iter_index) + app_info = list_view_model.get_value(row_iter, 0) + + gio_file = Gio.File.new_for_path('/tmp/tut07-appchooser-test.txt') + app_info.launch((gio_file,), None) + self.text.set_text(widget.get_name() + ' ' + app_info.get_name()) + + def scale(self, widget, value): + self.text.set_text(widget.get_name() + ' ' + str(value)) + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut07/tut07.glade b/bases/do-blog/resources/documents/python/gtk3/tut07/tut07.glade new file mode 100755 index 0000000..e6163ff --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut07/tut07.glade @@ -0,0 +1,101 @@ + + + + + False + + + True + False + vertical + + + True + False + + + True + False + True + none + True + False + 0.49000000953674316 + top + vertical + + + 0 + 0 + 1 + 1 + + + + + True + False + True + none + True + False + button + + + 0 + 1 + 1 + 1 + + + + + True + False + audio/flac + select app + + + 1 + 1 + 1 + 1 + + + + + True + False + text/plain + + + 1 + 0 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut08/tut08-menu-calendar.py b/bases/do-blog/resources/documents/python/gtk3/tut08/tut08-menu-calendar.py new file mode 100755 index 0000000..3d5ebfc --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut08/tut08-menu-calendar.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 calendar widget with menu] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 calendar example and drop down menu example] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gtk, GLib, GObject + + +class application_gui: + """Tutorial 08 menu, calendar widget.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut08.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['calendar'] = xml.get_object('calendar1') + self.widgets['menushow'] = xml.get_object('menuitem6') + self.widgets['menuhide'] = xml.get_object('menuitem7') + self.widgets['menuhello'] = xml.get_object('menuitem8') + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + self.widgets['menushow'].connect('activate', self.showcalendar) + self.widgets['menuhide'].connect('activate', self.hidecalendar) + self.widgets['menuhello'].connect('activate', self.hello) + self.widgets['calendar'].connect('day-selected', self.date_selected) + + #show the window else there is nothing to see :) + self.window.show() + + def hidecalendar(self, *args): + self.widgets['calendar'].hide() + + def showcalendar(self, *args): + self.widgets['calendar'].show() + + def hello(self, *args): + self.text.set_text('hello world') + + def date_selected(self, *args): + date = self.widgets['calendar'].get_date() + self.text.set_text(str(date[2]) + '-'+str(date[1]) + '-' + str(date[0])) + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut08/tut08.glade b/bases/do-blog/resources/documents/python/gtk3/tut08/tut08.glade new file mode 100755 index 0000000..44bcaa7 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut08/tut08.glade @@ -0,0 +1,235 @@ + + + + + + False + + + True + False + vertical + + + True + False + + + True + False + _File + True + + + True + False + + + gtk-new + True + False + True + True + + + + + gtk-open + True + False + True + True + + + + + gtk-save + True + False + True + True + + + + + gtk-save-as + True + False + True + True + + + + + True + False + + + + + gtk-quit + True + False + True + True + + + + + + + + + True + False + _Edit + True + + + True + False + + + gtk-cut + True + False + True + True + + + + + gtk-copy + True + False + True + True + + + + + gtk-paste + True + False + True + True + + + + + gtk-delete + True + False + True + True + + + + + + + + + True + False + _View + True + + + + + True + False + _Help + True + + + True + False + + + gtk-about + True + False + True + True + + + + + + + + + True + False + example + True + + + True + False + + + True + False + show + True + + + + + True + False + hide + True + + + + + True + False + hello world + True + + + + + + + + + False + True + 0 + + + + + True + True + 2014 + 2 + 31 + + + False + True + 1 + + + + + True + True + + + False + True + 2 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut09/tut09-treeview-listview.py b/bases/do-blog/resources/documents/python/gtk3/tut09/tut09-treeview-listview.py new file mode 100755 index 0000000..b8a520a --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut09/tut09-treeview-listview.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 treeview using a liststore] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 treeview in liststore, show a table like structure] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gtk, GLib, GObject + + +class application_gui: + """Tutorial 09 treeview in list mode.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut09.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['treeview'] = xml.get_object('treeview1') + treeview(self.widgets['treeview'], self.text) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + +class treeview: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, treeview, entry): + self.entry = entry + self.treeview = treeview + + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.mouse_click) + + #create a storage model in this case a treemodel + self.treemodel = Gtk.ListStore(str, str, str) + self.treeview.set_model(self.treemodel) + + #add columns usually only one in case of the treeview + column1 = Gtk.TreeViewColumn("Column 01") + self.treeview.append_column(column1) + + column2 = Gtk.TreeViewColumn("Column 02") + self.treeview.append_column(column2) + + column3 = Gtk.TreeViewColumn("Column 03") + self.treeview.append_column(column3) + + #add in a text renderer so we can see the items we add + cell = Gtk.CellRendererText() + column1.pack_start(cell, False) + column1.add_attribute(cell, "text", 0) + + cell = Gtk.CellRendererText() + column2.pack_start(cell, False) + column2.add_attribute(cell, "text", 0) + + cell = Gtk.CellRendererText() + column3.pack_start(cell, False) + column3.add_attribute(cell, "text", 0) + + self.populate() + self.menu() + + def populate(self): + self.treemodel.clear() + #populate the treeview with some list items + for item1 in range(0, 5): + iter_level_1 = self.append_tree('Item ' + str(item1)) + + def append_tree(self, name, parent=None): + """ + append to the treeview if parent is null append to root level. + if parent is a valid iter (possibly returned from previous append) then append under the parent + """ + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def menu(self): + """ + popover menu shown on right clicking a treeview item. + """ + self.treeview_menu = Gtk.Menu() + for item in range(0, 5): + menu_item = Gtk.MenuItem("Menu " + str(item)) + self.treeview_menu.append(menu_item) + + def mouse_click(self, tv, event): + if event.button == 3: + # right mouse button pressed popup the menu + self.treeview_menu.show_all() + self.treeview_menu.popup(None, None, None, None, 1, 0) + + def selection(self, tv, treepath, tvcolumn): + """ + on double click get the value of the item we clicked + """ + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + self.entry.set_text(self.selected) + + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut09/tut09.glade b/bases/do-blog/resources/documents/python/gtk3/tut09/tut09.glade new file mode 100755 index 0000000..6f1e5a9 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut09/tut09.glade @@ -0,0 +1,76 @@ + + + + + + + + + + + + Row1 + + + Row2 + + + Row3 + + + + + False + + + True + False + vertical + + + True + False + + + True + False + True + True + liststore1 + + + + + + 0 + 0 + 1 + 1 + + + + + False + True + 0 + + + + + True + True + + + False + True + 1 + + + + + + + True + False + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut10/tut10-treeview-treestore.py b/bases/do-blog/resources/documents/python/gtk3/tut10/tut10-treeview-treestore.py new file mode 100755 index 0000000..d6188d8 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut10/tut10-treeview-treestore.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 treeview using a treestore] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 treeview in treestore, show the data in a expandable hierarchy] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gtk, GLib + + +class application_gui: + """Tutorial 10 text input, display a treeview in expandable tree format.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut10.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['treeview'] = xml.get_object('treeview1') + treeview(self.widgets['treeview'], self.text) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + +class treeview: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, treeview, entry): + self.entry = entry + self.treeview = treeview + + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.mouse_click) + + #create a storage model in this case a treemodel + self.treemodel = Gtk.TreeStore(str) + self.treeview.set_model(self.treemodel) + + #add columns usually only one in case of the treeview + column = Gtk.TreeViewColumn("Objects") + self.treeview.append_column(column) + + #add in a text renderer so we can see the items we add + cell = Gtk.CellRendererText() + column.pack_start(cell, False) + column.add_attribute(cell, "text", 0) + + self.populate() + self.menu() + + def populate(self): + self.treemodel.clear() + #populate the treeview with a largish tree + for item1 in range(0, 5): + iter_level_1 = self.append_tree('Item ' + str(item1)) + for item2 in range(0, 5): + iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1) + for item3 in range(0, 5): + self.append_tree('Sub Sub Item ' + str(item3), iter_level_2) + + def append_tree(self, name, parent=None): + """ + append to the treeview if parent is null append to root level. + if parent is a valid iter (possibly returned from previous append) then append under the parent + """ + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def menu(self): + """ + popover menu shown on right clicking a treeview item. + """ + self.treeview_menu = Gtk.Menu() + for item in range(0, 5): + menu_item = Gtk.MenuItem("Menu " + str(item)) + self.treeview_menu.append(menu_item) + + def mouse_click(self, tv, event): + if event.button == 3: + # right mouse button pressed popup the menu + self.treeview_menu.show_all() + self.treeview_menu.popup(None, None, None, None, 1, 0) + + def selection(self, tv, treepath, tvcolumn): + """ + on double click get the value of the item we clicked + """ + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + self.entry.set_text(self.selected) + + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut10/tut10.glade b/bases/do-blog/resources/documents/python/gtk3/tut10/tut10.glade new file mode 100755 index 0000000..a4597de --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut10/tut10.glade @@ -0,0 +1,83 @@ + + + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + + + + + + + Row1 + + + Row2 + + + Row3 + + + + + + True + False + + + False + + + True + False + vertical + + + True + False + + + True + False + liststore1 + + + 0 + 0 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut11/tut11-interactive-python-textview.py b/bases/do-blog/resources/documents/python/gtk3/tut11/tut11-interactive-python-textview.py new file mode 100755 index 0000000..1795e5a --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut11/tut11-interactive-python-textview.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 interactive textview] +# [SNIPPET_CATEGORIES: gtk] +# [SNIPPET_TAGS:interactive, gtk3] +# [SNIPPET_DESCRIPTION: using gtk3 textview run python code on running program] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] + + +import sys +from StringIO import StringIO +from gi.repository import Gtk, Gdk +import code +import math + + +class interactiveGtk: + + def __init__(self): + window = Gtk.Window() + window.set_default_size(380, 300) + window.connect("destroy", lambda w: Gtk.main_quit()) + + box = Gtk.VBox() + + self.drawingarea = Gtk.DrawingArea() + + #python console using a textview + console = interactive_console(Gtk.TextView()) + self.drawingarea.connect("draw", self.area_expose_cb) + + box.add(self.drawingarea) + box.add(console.textarea) + + window.add(box) + window.show_all() + + self.drawarea = False + + def show_drawing(self, state): + """self.show_drawing(True) to enable showing the lines""" + self.drawarea = state + + def test(self): + """run app.test() when program is running to print this message""" + print ('hello world') + + def area_expose_cb(self, widget, context): + """expose event lets draw, lines will only display if we run self.show_lines first. + demonstrating running state change of our program""" + self.style = self.drawingarea.get_style() + if self.drawarea is True: + self.drawing(context, 210, 10) + + def drawing(self, cr, x, y): + """ draw a circle in the drawing area """ + cr.set_line_width(10) + cr.set_source_rgb(0.5, 0.8, 0.0) + + cr.translate(20 / 2, 20 / 2) + cr.arc(50, 50, 50, 0, 2 * math.pi) + cr.stroke_preserve() + + cr.set_source_rgb(0.3, 0.4, 0.4) + cr.fill() + + +class interactive_console: + editor_chars = '>>>' + editor_chars_other = '...' + editor_history = [] + editor_history_position = 0 + + def __init__(self, textview): + #the python editor window + self.textarea = textview + self.textarea.connect('key-press-event', self.key_pressed) + self.console_buffer = self.textarea.get_buffer() + + #setup some characters which can not be changed + self.console_buffer.set_text(self.editor_chars + 'app.show_drawing(True)') + self.console_buffer.create_tag("uneditable", editable=False, editable_set=True) + self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True) + self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False) + self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(len(self.editor_chars))) + + #interactive mode interpreter, + #pass locals() or globals() so we can access our programs functions and variables + #using global here so we have access to the app object in the global scope + self.interpreter = code.InteractiveInterpreter(globals()) + + def key_pressed(self, widget, event): + """ + grab key presses, run code from textview on return + navigate history if arrow keys are pressed + """ + if event.keyval == Gdk.keyval_from_name('Return'): + self.execute_line() + return True + + if event.keyval == Gdk.keyval_from_name('Up'): + self.console_history(-1) + return True + + if event.keyval == Gdk.keyval_from_name('Down'): + self.console_history(1) + return True + return False + + def execute_line(self): + """ + carriage return was captured so lets process the textview contents for code to run + """ + + text = self.console_buffer.get_text(self.console_buffer.get_start_iter(), self.console_buffer.get_end_iter(), False) + source = '' + block = False + indent = 0 + #work out code to run if its not a block or a blank line then run what we have, + #if its a block of code like a for loop or if condition dont run it yet unless the block has finished. + last_line = '' + for line in text.split("\n"): + line = line[3:].strip('') + if line.strip() == '': + block = False + indent = 0 + else: + if line.endswith(':'): + if block is True: + source += line + "\n\t" + else: + source = line + "\n\t" + block = True + indent += 1 + else: + if line.startswith("\t"): + source += line + "\n" + else: + block = False + source = line + "\n" + indent = 0 + last_line = line + if last_line.strip() != '': + self.append_history(last_line) + chars = self.editor_chars + + + # run the code grabbed from the text buffer and execute it if its not a block + results = '' + if block is True: + chars = self.editor_chars_other + else: + chars = self.editor_chars + results = self.execute_code(source) + + #build text for the editor, and workout which part of the text should be locked + text_output = text + '\n' + results + chars + text_output_length = len(text_output) + if block is True: + text_output += "\t" * indent + self.update_editor(text_output, text_output_length) + + def execute_code(self, source): + """ + run any code sent here and capture output and return the results + """ + # capture output from stdio so we can display the errors in our editor + result = StringIO() + sys.stdout = result + sys.stderr = result + #run code output will be put into result because we are capturing stdout + self.interpreter.runsource(source, "<>") + # restore stdout so future output is capture to the terminal again + sys.stdout = sys.__stdout__ + sys.stdout = sys.__stderr__ + return result.getvalue() + + def update_editor(self, text, uneditable_end=0): + """ + pass in text to put in the editor and portion to be locked from editing + """ + self.console_buffer.set_text(text) + self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True) + self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False) + self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(uneditable_end)) + + def append_history(self, line): + """ + Store command in history drop command if limit has been reached + """ + if len(self.editor_history) > 10: + self.editor_history.pop() + self.editor_history.append(line) + self.editor_history_position = len(self.editor_history) + + def console_history(self, direction=-1): + """ + drop any text on last line and insert text from history based on position + """ + # get current text excluding last line as we will update from our history + text = self.console_buffer.get_text( + self.console_buffer.get_start_iter(), + self.console_buffer.get_iter_at_line_index( + self.console_buffer.get_line_count(), 0), + False) + + #work out position in history and what should be displayed + linenumber = self.editor_history_position + direction + if linenumber >= 0 and linenumber < len(self.editor_history): + self.editor_history_position += direction + self.console_buffer.set_text(text + self.editor_chars + self.editor_history[self.editor_history_position]) + + +app = interactiveGtk() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut12/tut12-mixing-opengl-with-gtk.py b/bases/do-blog/resources/documents/python/gtk3/tut12/tut12-mixing-opengl-with-gtk.py new file mode 100755 index 0000000..d493a5d --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut12/tut12-mixing-opengl-with-gtk.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 opengl example] +# [SNIPPET_CATEGORIES: opengl] +# [SNIPPET_TAGS: opengl, gtk3] +# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] + +import sys +from OpenGL.GL import * +from OpenGL.GLU import * +from OpenGL import GLX +from OpenGL.raw._GLX import struct__XDisplay +from OpenGL import GL +from ctypes import * + +import Xlib +from Xlib.display import Display +from gi.repository import Gtk, GdkX11, Gdk + + +class gtkgl: + """ these method do not seem to exist in python x11 library lets exploit the c methods """ + xlib = cdll.LoadLibrary('libX11.so') + xlib.XOpenDisplay.argtypes = [c_char_p] + xlib.XOpenDisplay.restype = POINTER(struct__XDisplay) + xdisplay = xlib.XOpenDisplay(None) + display = Xlib.display.Display() + attrs = [] + + xwindow_id = None + width = height = 200 + + def __init__(self): + """ lets setup are opengl settings and create the context for our window """ + self.add_attribute(GLX.GLX_RGBA, True) + self.add_attribute(GLX.GLX_RED_SIZE, 1) + self.add_attribute(GLX.GLX_GREEN_SIZE, 1) + self.add_attribute(GLX.GLX_BLUE_SIZE, 1) + self.add_attribute(GLX.GLX_DOUBLEBUFFER, 0) + + xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes()) + configs = GLX.glXChooseFBConfig(self.xdisplay, 0, None, byref(c_int())) + self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True) + + def add_attribute(self, setting, value): + """just to nicely add opengl parameters""" + self.attrs.append(setting) + self.attrs.append(value) + + def get_attributes(self): + """ return our parameters in the expected structure""" + attrs = self.attrs + [0, 0] + return (c_int * len(attrs))(*attrs) + + def configure(self, wid): + """ """ + self.xwindow_id = GdkX11.X11Window.get_xid(wid) + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ('failed configuring context') + glViewport(0, 0, self.width, self.height) + + def draw_start(self): + """make cairo context current for drawing""" + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ("failed to get the context for drawing") + + def draw_finish(self): + """swap buffer when we have finished drawing""" + GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id) + + def test(self): + """Test method to draw something so we can make sure opengl is working and we can see something""" + self.draw_start() + + glClearColor(0.0, 0.0, 0.0, 0.0) + glClear(GL_COLOR_BUFFER_BIT) + glBegin(GL_TRIANGLES) + glIndexi(0) + glColor3f(1.0, 0.0, 0.0) + glVertex2i(0, 1) + glIndexi(0) + glColor3f(0.0, 1.0, 0.0) + glVertex2i(-1, -1) + glIndexi(0) + glColor3f(0.0, 0.0, 1.0) + glVertex2i(1, -1) + glEnd() + + self.draw_finish() + + +class gui(): + glwrap = gtkgl() + + def __init__(self): + """ create a gtk window, attach a drawing area and connect the relevant events""" + + self.window = Gtk.Window() + self.window.realize() + self.window.resize(self.glwrap.width, self.glwrap.height) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 with opengl") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.drawing_area = Gtk.DrawingArea() + self.drawing_area.connect('configure_event', self.on_configure_event) + self.drawing_area.connect('draw', self.on_draw) + self.drawing_area.set_double_buffered(False) + self.drawing_area.set_size_request(self.glwrap.width, self.glwrap.height) + + self.window.add(self.drawing_area) + self.window.show_all() + + def on_configure_event(self, widget, event): + """if we recieve a configure event for example a resize, then grab the context settings and resize our scene """ + self.glwrap.configure(widget.get_window()) + self.glwrap.test() + return True + + def on_draw(self, widget, context): + """if we recieve a draw event redraw our test triangle""" + self.glwrap.test() + + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut13/example.xml b/bases/do-blog/resources/documents/python/gtk3/tut13/example.xml new file mode 100755 index 0000000..b6b9342 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut13/example.xml @@ -0,0 +1,29 @@ + + + + + + Python FAQ + file:///usr/share/doc/python/FAQ.html + Example Feed reading into gtk + + Python README + file:///usr/share/doc/python/README.Debian + Example 01 Descriptive Text + + + + Python FAQ + file:///usr/share/doc/python/FAQ.html + Example 02 Descriptive Text + + + + Python FAQ + file:///usr/share/doc/python/FAQ.html + Example 02 Descriptive Text + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut13/tut13-listbox.py b/bases/do-blog/resources/documents/python/gtk3/tut13/tut13-listbox.py new file mode 100755 index 0000000..588da89 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut13/tut13-listbox.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 listbox example] +# [SNIPPET_CATEGORIES: opengl] +# [SNIPPET_TAGS: touch, gtk3] +# [SNIPPET_DESCRIPTION: add rows to a listbox based on retrieved xml file] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +import os +import requests +from io import StringIO, BytesIO +import subprocess +from lxml.html import parse +from gi.repository import Gtk, GLib, Gdk, GdkPixbuf + + +class application_gui: + """Tutorial 13 custom treeview list boxes""" + count = 0 + + retrieve_job = None + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut13.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('winFetcher') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['listbox'] = xml.get_object('listbox1') + self.widgets['progress'] = xml.get_object('listProgress') + self.widgets['refresh'] = xml.get_object('btnRefresh') + self.widgets['refresh'].connect('button_press_event', self.refresh) + self.widgets['close'] = xml.get_object('btnClose') + self.widgets['close'].connect('button_press_event', self.closeFetcher) + + #wrap the listbox so we can reuse the code, pass in the listbox widget to our wrapper class + self.listbox = ListBoxSelect(self.widgets['listbox']) + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.openFetcher() + self.refresh() + + def openFetcher(self): + self.window.show_all() + + def refresh(self, *args): + """ get a new xml and start the progress bar""" + self.listbox.clear() + self.widgets['progress'].show() + self.retrieve_job = subprocess.Popen( + ['curl', 'file://%s/example.xml' % os.path.abspath('./')], + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + GLib.timeout_add_seconds(1, self.update_active_progress_bar) + + def update_active_progress_bar(self): + """ move the progress bar, when the subprocess returns handle the xml and hide the progress bar""" + self.widgets['progress'].pulse() + if self.retrieve_job.poll(): + return True + self.widgets['progress'].hide() + self.update_list() + return False + + def update_list(self): + """ parse the xmland grab the elements we are intrested in""" + nsmap = {'media': 'http://search.yahoo.com/mrss/'} + results = BytesIO(self.retrieve_job.communicate()[0]) + doc = parse(results).getroot() + for item in doc.iterfind(".//item", namespaces=nsmap): + title = item.find('title').text + link = item.find('link').tail + description = item.find('description').text + image = item.find('thumbnail', namespaces=nsmap).get('url') + self.listbox.model_append(image, title, description, link) + + def download(self): + """ retrieve the xml file in a subprocess using curl """ + self.retrieve_job = subprocess.Popen( + ['curl', 'file://%s/example.xml' % os.path.abspath('./')], + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + def closeFetcher(self, widget): + self.window.hide() + +class ListBoxSelect: + """ handle the listbox rows dynamically add and remove widgets, and handle download. """ + listbox = None + gui_rows = [] # store widgets here so we can destroy them later. + + def __init__(self, listbox): + """ pass in list box to manage and connect event""" + self.listbox = listbox + self.listbox.connect('row-activated', self.selection) + + def selection(self, lbox, lbrow): + """ row selected we may want to react here""" + boxrow = lbrow.get_children()[0] + boxinfo = boxrow.get_children()[1] + print(boxinfo.get_children()[1].get_text()) + + def model_append(self, image, title, description, link): + """ create new widgets, and connect events for our new row""" + items = {} + items['row'] = Gtk.ListBoxRow() + items['vbox'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + items['label1'] = Gtk.Label(title, xalign=0) + items['label2'] = Gtk.Label(link, xalign=0) + items['progress'] = Gtk.ProgressBar() + items['progress'].hide() + items['progress'].set_fraction(0) + items['vbox'].pack_start(items['label1'], True, False, 0) + items['vbox'].pack_start(items['label2'], True, False, 0) + items['vbox'].pack_start(items['progress'], False, False, 0) + + items['hbox'] = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + items['image'] = Gtk.Image.new_from_file(image) + items['button'] = Gtk.Button(label="Download") + items['button'].connect('button_press_event', self.download, items, link) + + items['hbox'].pack_start(items['image'], False, False, 0) + items['hbox'].pack_start(items['vbox'], True, True, 0) + items['button_box'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + + items['button_box'].pack_end(items['button'], False, False, 0) + items['hbox'].pack_start(items['button_box'], False, False, 0) + items['row'].add(items['hbox']) + + self.listbox.add(items['row']) + items['row'].show_all() + + self.gui_rows.append(items) + + def download(self, widget, args, items, link): + """ download button click, change widgets and start the progress bar and download """ + items['button'].hide() + items['job'] = subprocess.Popen( + ['curl', '-O', link], + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + GLib.timeout_add_seconds(1, self.update_active_progress_bar, items) + + def update_active_progress_bar(self, widgets): + """ update progress bar until command finished """ + widgets['progress'].pulse() + if widgets['job'].poll(): + return True + widgets['progress'].hide() + return False + + def clear(self): + """ remove all rows so we can pre-populate""" + for item in self.gui_rows: + item['row'].destroy() + self.gui_rows = [] + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut13/tut13.glade b/bases/do-blog/resources/documents/python/gtk3/tut13/tut13.glade new file mode 100755 index 0000000..20b4298 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut13/tut13.glade @@ -0,0 +1,114 @@ + + + + + + 400 + 400 + False + + + True + False + vertical + + + True + False + + + True + True + + + True + True + 0 + + + + + gtk-refresh + True + True + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + True + True + in + + + True + False + + + True + False + + + + + + + True + True + 2 + + + + + True + False + + + Close + True + True + True + half + + + False + True + end + 2 + + + + + False + True + 3 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut14/tut14-touchscreen-events.py b/bases/do-blog/resources/documents/python/gtk3/tut14/tut14-touchscreen-events.py new file mode 100755 index 0000000..7565960 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut14/tut14-touchscreen-events.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 touch example] +# [SNIPPET_CATEGORIES: opengl] +# [SNIPPET_TAGS: touch, gtk3] +# [SNIPPET_DESCRIPTION: handling touch events with GTK3] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] + +import sys +from gi.repository import Gtk, GdkX11, Gdk + + +class gui(): + + def __init__(self): + self.touch_count = 0 + self.window = Gtk.Window() + self.window.realize() + self.window.resize(300, 300) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 touch events") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.drawing_area = Gtk.DrawingArea() + #add the type of events we are interested in retrieving, skip this step and your events will never fire + self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK) + self.drawing_area.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) + self.drawing_area.connect('button_press_event', self.touched) + self.drawing_area.connect('touch-event', self.touched) + self.drawing_area.set_double_buffered(False) + + self.window.add(self.drawing_area) + self.window.show_all() + + def touched(self, widget, ev): + # detect the type of device we can filter events using this + # if we dont a touch event and mouse event can be triggered from one touch for example + if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHPAD: + print('touchpad device') + if ev.get_source_device().get_source() == Gdk.InputSource.MOUSE: + print('mouse device') + if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN: + print('touchscreen device') + + #from what i can tell there is no support for gestures so you would need another library + #or to handle this yourself + if ev.touch.type == Gdk.EventType.TOUCH_BEGIN: + self.touch_count += 1 + print('start %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y)) + if ev.touch.type == Gdk.EventType.TOUCH_UPDATE: + print('UPDATE %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y)) + if ev.touch.type == Gdk.EventType.TOUCH_END: + self.touch_count -= 1 + print('end %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y)) + if ev.touch.type == Gdk.EventType.TOUCH_CANCEL: + self.touch_count -= 1 + print('cancelled') + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut14/tut14.glade b/bases/do-blog/resources/documents/python/gtk3/tut14/tut14.glade new file mode 100755 index 0000000..f504862 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut14/tut14.glade @@ -0,0 +1,41 @@ + + + + + + False + + + True + False + vertical + + + True + False + GDK_STRUCTURE_MASK | GDK_TOUCH_MASK + + + True + True + 0 + + + + + True + True + + + False + True + 1 + + + + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut15/gui.glade b/bases/do-blog/resources/documents/python/gtk3/tut15/gui.glade new file mode 100755 index 0000000..43659a9 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut15/gui.glade @@ -0,0 +1,168 @@ + + + + + + 0.050000000000000003 + 10 + 0.050000000000000003 + 0.050000000000000003 + 10 + + + 1 + 20 + 1 + 10 + + + False + + + True + False + + + True + False + + + True + False + Radius + + + 0 + 3 + 1 + 1 + + + + + True + True + adjustment2 + 1 + + + 1 + 3 + 1 + 1 + + + + + True + False + + + 0 + 4 + 1 + 1 + + + + + generate + True + True + True + + + 1 + 4 + 1 + 1 + + + + + True + False + Rotation + + + 0 + 2 + 1 + 1 + + + + + True + True + adjustment1 + 0.050000000000000003 + 2 + 0.050000000000000003 + + + 1 + 2 + 1 + 1 + + + + + True + False + Mode + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + + VBO per cube + VBO all cubes + VBO Test Cube + + + + 1 + 1 + 1 + 1 + + + + + + + + + + + False + True + 0 + + + + + True + False + + + True + True + 1 + + + + + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut15/helper.py b/bases/do-blog/resources/documents/python/gtk3/tut15/helper.py new file mode 100755 index 0000000..7845434 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut15/helper.py @@ -0,0 +1,173 @@ +import numpy +from numpy import array +from OpenGL.GL import * +from OpenGL.GL import shaders +from OpenGL.arrays import vbo + +from OpenGL._bytes import bytes, _NULL_8_BYTE + +def compileShader( source, shaderType ): + + """Compile shader source of given type + source -- GLSL source-code for the shader + shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc, + returns GLuint compiled shader reference + raises RuntimeError when a compilation failure occurs + """ + if isinstance(source, str): + + source = [source] + elif isinstance(source, bytes): + + source = [source.decode('utf-8')] + + shader = glCreateShader(shaderType) + glShaderSource(shader, source) + glCompileShader(shader) + result = glGetShaderiv(shader, GL_COMPILE_STATUS) + + if not(result): + # TODO: this will be wrong if the user has + # disabled traditional unpacking array support. + raise RuntimeError( + """Shader compile failure (%s): %s"""%( + result, + glGetShaderInfoLog( shader ), + ), + source, + shaderType, + ) + return shader + + +class shader: + vertex = """#version 120 + //attributes in values + attribute vec3 inPosition; + attribute vec3 inColor; + + //varying sending to fragment shader + varying vec4 outColor; + void main(){ + vec4 fragmentPos = gl_ModelViewMatrix * gl_Vertex; + gl_Position = (gl_ProjectionMatrix * fragmentPos); + outColor = vec4(gl_Color.rgb, 1); + }""" + + fragment = """#version 120 + varying vec4 outColor; + void main(){ + gl_FragColor = outColor; + }""" + + program = None + def compile(self): + self.program = shaders.compileProgram( + compileShader(self.vertex, GL_VERTEX_SHADER), + compileShader(self.fragment, GL_FRAGMENT_SHADER),) + +class point: + __slots__ = ['x', 'y', 'z', 'xyz', 'vertex'] + + def __init__(self, p, c=(1, 0, 0)): + """ Position in 3d space as a tuple or list, and colour in tuple or list format""" + self.x, self.y, self.z = p + self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f') + + +class cube: + def __init__(self, p1, color, size=0.5): + self.color = array([1, 0, 0], 'f') + #self.xyz = p1 + self.points = ( + point((p1[0] - size, p1[1] + size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] - size), (color)), + + point((p1[0] - size, p1[1] - size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] - size), (color)), + + ) + + self.vbuffer = vbo.VBO(array(self.get_data(), 'f')) + self.vbuffer_size = len(self.get_data()) + + def bind(self): + self.vbuffer.bind() + + def unbind(self): + self.vbuffer.unbind() + + def get_data(self): + return ( + self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, + self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, + + self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, + self.points[0].vertex, self.points[5].vertex, self.points[4].vertex, + + self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, + self.points[0].vertex, self.points[4].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, + self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[1].vertex, self.points[2].vertex, + self.points[6].vertex, self.points[5].vertex, self.points[1].vertex, + + self.points[6].vertex, self.points[4].vertex, self.points[5].vertex, + self.points[6].vertex, self.points[7].vertex, self.points[4].vertex, + ) + + + +class cube: + def __init__(self, p1, color, size=0.5): + self.color = array([1, 0, 0], 'f') + #self.xyz = p1 + self.points = ( + point((p1[0] - size, p1[1] + size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] - size), (color)), + + point((p1[0] - size, p1[1] - size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] - size), (color)), + + ) + + self.vbuffer = vbo.VBO(array(self.get_data(), 'f')) + self.vbuffer_size = len(self.get_data()) + + def bind(self): + self.vbuffer.bind() + + def unbind(self): + self.vbuffer.unbind() + + def get_data(self): + return ( + self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, + self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, + + self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, + self.points[0].vertex, self.points[5].vertex, self.points[4].vertex, + + self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, + self.points[0].vertex, self.points[4].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, + self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[1].vertex, self.points[2].vertex, + self.points[6].vertex, self.points[5].vertex, self.points[1].vertex, + + self.points[6].vertex, self.points[4].vertex, self.points[5].vertex, + self.points[6].vertex, self.points[7].vertex, self.points[4].vertex, + ) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut15/tut15-opengl-touch-app-01.py b/bases/do-blog/resources/documents/python/gtk3/tut15/tut15-opengl-touch-app-01.py new file mode 100755 index 0000000..07e0e27 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut15/tut15-opengl-touch-app-01.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python +import sys +import time +import random +import pprint + +import Xlib +from Xlib.display import Display +from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject +from ctypes import * +from numpy import array + +from OpenGL.GL import * +from OpenGL.GL import shaders +from OpenGL.GLU import gluPerspective, gluLookAt +from OpenGL.arrays import vbo +from OpenGL import GLX +try: + from OpenGL.GLX import struct__XDisplay +except ImportError as err: + from OpenGL.raw._GLX import struct__XDisplay +from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER +from OpenGL.GL import shaders, glGetUniformLocation + +from helper import shader +from helper import cube as createcube + +class gtkgl: + """ these method do not seem to exist in python x11 library lets exploit the c methods + useful link http://www.opengl.org/wiki/Programming_OpenGL_in_Linux:_GLX_and_Xlib""" + xlib = cdll.LoadLibrary('libX11.so') + xlib.XOpenDisplay.argtypes = [c_char_p] + xlib.XOpenDisplay.restype = POINTER(struct__XDisplay) + xdisplay = xlib.XOpenDisplay(None) + display = Xlib.display.Display() + attrs = [] + + xwindow_id = None + width = height = 500 + + def __init__(self): + """ lets setup are opengl settings and create the context for our window """ + self.add_attribute(GLX.GLX_RGBA, True) + self.add_attribute(GLX.GLX_RED_SIZE, 8) + self.add_attribute(GLX.GLX_GREEN_SIZE, 8) + self.add_attribute(GLX.GLX_BLUE_SIZE, 8) + self.add_attribute(GLX.GLX_DOUBLEBUFFER, 1) + self.add_attribute(GLX.GLX_DEPTH_SIZE, 24) + + xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes()) + print("run glxinfo to match this visual id %s " % hex(xvinfo.contents.visualid)) + self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True) + + def add_attribute(self, setting, value): + """just to nicely add opengl parameters""" + self.attrs.append(setting) + self.attrs.append(value) + + def get_attributes(self): + """ return our parameters in the expected structure""" + attrs = self.attrs + [0, 0] + return (c_int * len(attrs))(*attrs) + + def configure(self, wid): + """ """ + self.xwindow_id = GdkX11.X11Window.get_xid(wid) + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ('failed configuring context') + glViewport(0, 0, self.width, self.height) + + def draw_start(self): + """make cairo context current for drawing""" + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ("failed to get the context for drawing") + + def draw_finish(self): + """swap buffer when we have finished drawing""" + GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id) + +class scene: + width, height = 600, 600 + + rotationx = 0.0 + rotationy = 0.0 + + rotation_incx = 0.5 + rotation_incy = 0.5 + radius = 0 + + touch_count = 0 + touch_previous = 0, 0 + touch_start_one = 0, 0 + touch_start_two = 0, 0 + touch_end_one = 0, 0 + touch_end_two = 0, 0 + touch_time = 0 + + camera_distance = 25 + + cube_length = 1.0 + cube_size = cube_length / 2 + + def __init__(self): + """setup everything in the correct order""" + self.glwrap = gtkgl() + self.setup_opengl() + self.generate() + self.gui() + + self.mode = 'vbo all cubes' + + def gui(self): + """load in the gui and connect the events and set our properties""" + self.start_time = time.time() + self.frame = 1 + xml = Gtk.Builder() + xml.add_from_file('gui.glade') + + self.window = xml.get_object('window1') + + self.mode_widget = xml.get_object('cmbmode') + self.mode_widget.connect('changed', self.change_mode) + + self.rotate_widget = xml.get_object('spinrotate') + self.rotate_widget.connect('value-changed', self.change_rotate_speed) + + self.radius_widget = xml.get_object('spinradius') + self.radius_widget.connect('value-changed', self.change_radius) + + self.color_widget = xml.get_object('btncolor') + self.color_widget.connect('clicked', self.change_color) + + self.canvas_widget = xml.get_object('canvas') + self.canvas_widget.connect('configure_event', self.on_configure_event) + self.canvas_widget.connect('draw', self.on_draw) + self.canvas_widget.set_double_buffered(False) + self.canvas_widget.set_size_request(self.glwrap.width, self.glwrap.height) + self.canvas_widget.add_events(Gdk.EventMask.TOUCH_MASK) + self.canvas_widget.connect('touch-event', self.touched) + + self.window.show_all() + GObject.idle_add(self.loop_draw) + + def touched(self, widget, ev): + """basic touch support, count the touches so we no how many fingers + basic pinc zoom along the x, single finger slide to rotate""" + if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN: + if ev.touch.type == Gdk.EventType.TOUCH_BEGIN: + self.touch_start = ev.touch.x, ev.touch.y + self.touch_count += 1 + if self.touch_count == 2: + self.touch_start_two = ev.touch.x, ev.touch.y + self.touch_previous = ev.touch.x, ev.touch.y + + if ev.touch.type == Gdk.EventType.TOUCH_UPDATE: + if ev.touch.time - self.touch_time < 100: + return True + + if self.touch_count == 2: + #basic pinch zoom along the x axis + d1 = self.touch_previous[0] - ev.touch.x + if d1 > 1: + self.camera_distance += self.camera_distance * 0.05 + self.touch_previous = ev.touch.x, ev.touch.y + if d1 < 1: + self.camera_distance -= self.camera_distance * 0.05 + self.touch_previous = ev.touch.x, ev.touch.y + self.update_camera() + self.touch_time = ev.touch.time + + if ev.touch.type == Gdk.EventType.TOUCH_END: + self.touch_end = ev.touch.x, ev.touch.y + #set rotation when touch ends + if self.touch_count == 1: + self.rotation_incx = (self.touch_start[0] - self.touch_end[0]) * 0.01 + self.rotation_incy = (self.touch_start[1] - self.touch_end[1]) * 0.01 + self.touch_count = 0 + + def in_circle(self, center_x, center_y, center_z, radius, x, y, z): + """ test if our cordinate lies inside our sphere""" + square_dist = (center_x - x) ** 2 + (center_y - y) ** 2 + (center_z - z) ** 2 + return square_dist <= radius ** 2 + + def change_color(self, widget): + #regenerate the scene + self.generate() + + def change_mode(self, widget): + #change whats drawn and how + self.mode = widget.get_active_text().lower() + print(widget.get_active_text()) + + def change_rotate_speed(self, widget): + #handle spinner rotation speed event + self.rotation_incx = widget.get_value() + self.rotation_incy = widget.get_value() + + def change_radius(self, widget): + #increase size of circle and number of polygons + self.radius = int(widget.get_value()) + self.generate() + + def loop_draw(self): + #send redraw event to drawing area widget + self.canvas_widget.queue_draw() + return True + + def on_configure_event(self, widget, event): + """if we recieve a configure event for example a resize, then grab the context settings and resize our scene """ + self.glwrap.width = widget.get_allocation().width + self.glwrap.height = widget.get_allocation().height + self.width, self.height = self.glwrap.width, self.glwrap.height + + #update our states because we have reconfigured the display + self.glwrap.configure(widget.get_window()) + self.glwrap.draw_start() + self.update_camera() + self.setup_shaders() + + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDepthFunc(GL_LEQUAL) + glDepthRange(0.0, 1.0) + glEnable(GL_CULL_FACE) + glCullFace(GL_BACK) + glFrontFace(GL_CW) + + self.glwrap.draw_finish() + return True + + def on_draw(self, widget, context): + """if we recieve a draw event redraw our opengl scene""" + self.elapsed_time = time.time() - self.start_time + self.frame += 1 + + if self.elapsed_time > 1: + print('fps %d' % self.frame) + self.start_time = time.time() + self.frame = 1 + self.glwrap.draw_start() + self.draw() + self.glwrap.draw_finish() + + def generate(self): + self.cubes = [] + #position cubes inside a sphere radius + for shift_x in range(-self.radius, self.radius + 1): + for shift_y in range(-self.radius, self.radius + 1): + for shift_z in range(-self.radius, self.radius + 1): + x = shift_x * self.cube_length + y = shift_y * self.cube_length + z = shift_z * self.cube_length + if not self.in_circle(0, 0, 0, self.radius, x, y, z): + continue + #random colours / textures if we want + color = random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15]) + self.cubes.append(createcube((x, y, z), color, self.cube_size)) + + self.test_cube = createcube((x, y, z), (random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15])), 6) + faces = [] + for cube in self.cubes: + faces += cube.get_data() + + print('Generated %s Cubes' % str(len(self.cubes))) + print('Generated %s Tringles' % str(len(faces) / 3)) + self.vbuffer = vbo.VBO(array(faces, 'f')) + + def setup_shaders(self): + self.shader_program = shader() + self.shader_program.compile() + + def setup_opengl(self): + glShadeModel(GL_SMOOTH) + glClearColor(0.0, 0.0, 0.0, 0.0) + glClearDepth(1.0) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) + glPolygonMode(GL_FRONT, GL_FILL) + + def update_camera(self): + glViewport(0, 0, self.width, self.height) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluPerspective(45, 1.0 * self.width / self.height, 1.0, 80.0) + gluLookAt(self.camera_distance, self.camera_distance, self.camera_distance, # location + 0.0, 0.0, 0.0, # lookat + 0.0, 1.0, 0.0) # up direction + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + + def draw_test(self): + #lets do a simple rotation so we can see the objects are 3d + glRotatef(self.rotationx, 1.0, 0.0, 0.0) + self.rotationx += self.rotation_incx + glRotatef(self.rotationy, 0.0, 1.0, 0.0) + self.rotationy += self.rotation_incy + + #use our shader program and enable vertex loading + glUseProgram(self.shader_program.program) + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_COLOR_ARRAY) + + #render the triangles into a virtual buffer object + self.test_cube.bind() + glVertexPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer) + glColorPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer + 12) + glDrawArrays(GL_TRIANGLES, 0, self.test_cube.vbuffer_size) + self.test_cube.unbind() + + #restore opengl to our previous state + glDisableClientState(GL_COLOR_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + shaders.glUseProgram(0) + + def draw_vbo_per_cube(self): + #lets do a simple rotation so we can see the objects are 3d + glRotatef(self.rotationx, 1.0, 0.0, 0.0) + self.rotationx += self.rotation_incx + glRotatef(self.rotationy, 0.0, 1.0, 0.0) + self.rotationy += self.rotation_incy + + # use our shader program and enable vertex loading + glUseProgram(self.shader_program.program) + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_COLOR_ARRAY) + + # render the triangles into a virtual buffer object + for shape in self.cubes: + shape.bind() + glVertexPointer(3, GL_FLOAT, 24, shape.vbuffer) + glColorPointer(3, GL_FLOAT, 24, shape.vbuffer + 12) + glDrawArrays(GL_TRIANGLES, 0, shape.vbuffer_size) + shape.unbind() + + #restore opengl to our previous state + glDisableClientState(GL_COLOR_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + shaders.glUseProgram(0) + + def draw_vbo_all_cubes(self): + #lets do a simple rotation so we can see the objects are 3d + glRotatef(self.rotationx, 1.0, 0.0, 0.0) + self.rotationx += self.rotation_incx + glRotatef(self.rotationy, 0.0, 1.0, 0.0) + self.rotationy += self.rotation_incy + + # use our shader program and enable vertex loading + glUseProgram(self.shader_program.program) + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_COLOR_ARRAY) + + # render the triangles into a virtual buffer object + self.vbuffer.bind() + glVertexPointer(3, GL_FLOAT, 24, self.vbuffer) + glColorPointer(3, GL_FLOAT, 24, self.vbuffer + 12) + glDrawArrays(GL_TRIANGLES, 0, len(self.vbuffer)) + self.vbuffer.unbind() + + #restore opengl to our previous state + glDisableClientState(GL_COLOR_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + shaders.glUseProgram(0) + + def draw(self): + glEnable(GL_DEPTH_TEST) + glClearDepth(1.0) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + if self.mode == 'vbo test cube': + self.draw_test() + if self.mode == 'vbo per cube': + self.draw_vbo_per_cube() + if self.mode == 'vbo all cubes': + self.draw_vbo_all_cubes() + + +if __name__ == '__main__': + glexample = scene() + GLib.threads_init() + Gdk.threads_init() + Gdk.threads_enter() + Gtk.main() + Gdk.threads_leave() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut16-wip/tut16-cairo.py b/bases/do-blog/resources/documents/python/gtk3/tut16-wip/tut16-cairo.py new file mode 100755 index 0000000..ba44be4 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut16-wip/tut16-cairo.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 opengl example] +# [SNIPPET_CATEGORIES: opengl] +# [SNIPPET_TAGS: opengl, gtk3] +# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] + +import sys +from gi.repository import Gtk, GdkX11, Gdk +import cairo + + +class gui(): + + def __init__(self): + self.window = Gtk.Window() + self.window.realize() + self.window.resize(300, 300) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 with cairo") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.drawing_area = Gtk.DrawingArea() + self.drawing_area.set_size_request(50,50) + + self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK) + self.drawing_area.connect('draw', self.expose) + self.drawing_area.set_double_buffered(False) + + self.window.add(self.drawing_area) + self.window.show_all() + + def expose(self, widget, context): + context.set_source_rgb(1, 0.5, 1) + context.rectangle(10, 10, 40, 40) + + context.move_to(0, 0) + context.rel_line_to(100, 100) + context.fill() + context.save() + + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut16-wip/tut16-canvas.py b/bases/do-blog/resources/documents/python/gtk3/tut16-wip/tut16-canvas.py new file mode 100755 index 0000000..ba44be4 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut16-wip/tut16-canvas.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 opengl example] +# [SNIPPET_CATEGORIES: opengl] +# [SNIPPET_TAGS: opengl, gtk3] +# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] + +import sys +from gi.repository import Gtk, GdkX11, Gdk +import cairo + + +class gui(): + + def __init__(self): + self.window = Gtk.Window() + self.window.realize() + self.window.resize(300, 300) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 with cairo") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.drawing_area = Gtk.DrawingArea() + self.drawing_area.set_size_request(50,50) + + self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK) + self.drawing_area.connect('draw', self.expose) + self.drawing_area.set_double_buffered(False) + + self.window.add(self.drawing_area) + self.window.show_all() + + def expose(self, widget, context): + context.set_source_rgb(1, 0.5, 1) + context.rectangle(10, 10, 40, 40) + + context.move_to(0, 0) + context.rel_line_to(100, 100) + context.fill() + context.save() + + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut17-wip/tut15.py b/bases/do-blog/resources/documents/python/gtk3/tut17-wip/tut15.py new file mode 100755 index 0000000..d31a4d3 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut17-wip/tut15.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 opengl example] +# [SNIPPET_CATEGORIES: opengl] +# [SNIPPET_TAGS: opengl, gtk3] +# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] + +import sys +from gi.repository import Gtk, GdkX11, Gdk +import cairo + + +class gui(): + + def __init__(self): + self.window = Gtk.Window() + self.window.realize() + self.window.resize(300, 300) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 with opengl") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.fixed_area = Gtk.Layout() + self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE) + self.fixed_area.connect("drag-data-received", self.on_drag_data_received) + self.fixed_area.connect("drag-drop", self.on_drag_data_received) + self.fixed_area.connect("drag-motion", self.on_drag_data_received) + + + self.drawing_area = Gtk.DrawingArea() + self.drawing_area.set_size_request(50,50) + #self.drawing_area.set_border_width(50) + self.drawing_area.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],Gdk.DragAction.MOVE) + + #self.drawing_area.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [],DRAG_ACTION) + self.drawing_area.connect("drag-data-get", self.on_drag_data_get) + self.drawing_area.connect("drag-begin", self.on_drag_data_get) + self.drawing_area.connect("drag-end", self.on_drag_finish) + + #~ self.drawing_area.add_events(Gdk.EventMask.POINTER_MOTION_MASK) + #~ self.drawing_area.add_events(Gdk.EventMask.SCROLL_MASK) + self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK) + #self.drawing_area.connect('configure_event', self.on_configure_event) + #self.drawing_area.connect('draw', self.on_draw) + self.drawing_area.connect('draw', self.expose) + #self.drawing_area.connect('clicked', self.clicked) + + + self.drawing_area.set_double_buffered(False) + + self.fixed_area.put(self.drawing_area,1,1) + self.window.add(self.fixed_area) + self.fixed_area.show_all() + #self.window.add(self.drawing_area) + print 'test' + self.window.show_all() + + def on_drag_data_received(self, widget, drag_context, x,y, data,info, time): + print 'drop' + + def on_drag_finish(self, widget, event): + print event + print dir(event.get_dest_window) + print 'drop' + + def on_drag_data_get(self, widget, event): + print 'drag' + + def on_configure_event(self, widget, event): + return True + + def expose(self, widget, context): + #self.context = widget.window.cairo_create() + #context.rectangle(event.area.x, event.area.y, + # event.area.width, event.area.height) + #context.clip() + context.set_source_rgb(1, 0.5, 1) + context.rectangle(10, 10, 40, 40) + + context.move_to(0, 0) + context.rel_line_to(100, 100) + context.fill() + context.save() + + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut17-wip/tut17-drag-drop-cairo.py b/bases/do-blog/resources/documents/python/gtk3/tut17-wip/tut17-drag-drop-cairo.py new file mode 100755 index 0000000..f0c2a41 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut17-wip/tut17-drag-drop-cairo.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 opengl example] +# [SNIPPET_CATEGORIES: opengl] +# [SNIPPET_TAGS: opengl, gtk3] +# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] + +import sys +from gi.repository import Gtk, GdkX11, Gdk +import cairo + + +class gui(): + x = 10 + y = 10 + current_x = 0 + current_y = 0 + def __init__(self): + self.window = Gtk.Window() + self.window.realize() + self.window.resize(300, 300) + self.window.set_resizable(True) + self.window.set_reallocate_redraws(True) + self.window.set_title("GTK3 with opengl") + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + self.fixed_area = Gtk.Layout() + self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE) + self.fixed_area.connect("drag-data-received", self.on_drag_data_received) + self.fixed_area.connect("drag-drop", self.on_drag_data_received) + self.fixed_area.connect("drag-motion", self.on_drag_data_received) + + + self.drawing_area = Gtk.DrawingArea() + self.drawing_area.set_size_request(50,50) + self.drawing_area.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],Gdk.DragAction.MOVE) + + self.drawing_area.connect("drag-data-get", self.on_drag_data_get) + self.drawing_area.connect("drag-begin", self.on_drag_data_get) + self.drawing_area.connect("drag-end", self.on_drag_finish) + self.drawing_area.connect("motion-notify-event", self.move) + + self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK) + self.drawing_area.connect('draw', self.expose) + + self.drawing_area.set_double_buffered(False) + + self.fixed_area.put(self.drawing_area, 1, 1) + self.window.add(self.fixed_area) + self.fixed_area.show_all() + self.window.show_all() + + def on_drag_data_received(self, widget, drag_context, x,y, data,info, time): + self.x = x + self.y = y + print 'drop recieve' + print x + print y + + def on_drag_finish(self, widget, event): + print event + print dir(event.get_dest_window) + print event.get_dest_window.get_type() + self.x = self.current_x + self.y = self.current_y + print 'drop finish' + + def on_drag_data_get(self, widget, event): + print 'drag' + + def on_configure_event(self, widget, event): + return True + + def move(self, widget, event): + self.current_x = event.x + self.current_y = event.y + print self.x + + def expose(self, widget, context): + print 'expose' + print self.x + context.set_source_rgb(1, 0.5, 1) + context.move_to(self.x, self.y) + context.rectangle(self.x, self.y, self.x+40, self.y+40) + + + #context.rel_line_to(100, 100) + context.fill() + context.save() + + +def main(): + g = gui() + Gtk.main() + +if __name__ == '__main__': + main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut10-treeview-treestore-drag.py b/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut10-treeview-treestore-drag.py new file mode 100755 index 0000000..6938ff4 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut10-treeview-treestore-drag.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 treeview using a treestore] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 treeview in treestore, show the data in a expandable hierarchy] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gdk, Gtk, GLib + +#http://jason.zwolak.org/technoblog/2010/08/drag-and-drop-with-gtk2-and-perl/ +class application_gui: + """Tutorial 10 text input, display a treeview in expandable tree format.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut10.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['treeview'] = xml.get_object('treeview1') + treeview(self.widgets['treeview'], self.text) + + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + +class treeview: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, treeview, entry): + self.entry = entry + self.treeview = treeview + + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.mouse_click) + + #self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE) + self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.COPY) + self.treeview.enable_model_drag_dest([("example", 0, 0)], Gdk.DragAction.COPY) + #treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.ACTION_COPY) + #treeview.enable_model_drag_dest([("example", 0, 0)], gtk.gdk.ACTION_COPY) + + self.treeview.connect("drag_data_received", self.onDragDataReceived) + + + #create a storage model in this case a treemodel + self.treemodel = Gtk.TreeStore(str) + self.treeview.set_model(self.treemodel) + + #add columns usually only one in case of the treeview + column = Gtk.TreeViewColumn("Objects") + self.treeview.append_column(column) + + #add in a text renderer so we can see the items we add + cell = Gtk.CellRendererText() + column.pack_start(cell, False) + column.add_attribute(cell, "text", 0) + + self.populate() + self.menu() + + #~ def iterCopy(self, treeview, model, iter_to_copy, target_iter, pos): + #~ + #~ data_column_0 = model.get_value(iter_to_copy, 0) + #~ data_column_1 = model.get_value(iter_to_copy, 1) + #~ if (pos == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) or (pos == gtk.TREE_VIEW_DROP_INTO_OR_AFTER): + #~ new_iter = model.prepend(target_iter, None) + #~ elif pos == gtk.TREE_VIEW_DROP_BEFORE: + #~ new_iter = model.insert_before(None, target_iter) + #~ elif pos == gtk.TREE_VIEW_DROP_AFTER: + #~ new_iter = model.insert_after(None, target_iter) + #~ model.set_value(new_iter, 0, data_column_0) + #~ model.set_value(new_iter, 1, data_column_1) + #~ if model.iter_has_child(iter_to_copy): + #~ for i in range(0, model.iter_n_children(iter_to_copy)): + #~ next_iter_to_copy = model.iter_nth_child(iter_to_copy, i) + #~ self.iterCopy(treeview, model, next_iter_to_copy, new_iter, gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) + + def walk(self, treeiter): + if model.iter_has_child(treeiter): + for i in range(0, model.iter_n_children(treeiter)): + yield model.iter_nth_child(treeiter, i) + + def onDragDataReceived(self, treeview, drag_context, x, y, selection, info, eventtime): + """on data recieve we remove the item and insert in the new location, the insert location must have the same depth""" + model, iter_source = treeview.get_selection().get_selected() + source_path = model.get_path(iter_source) + print 'source iter path %s' % source_path + destination_path, pos = treeview.get_dest_row_at_pos(x, y) + print 'destination iter path %s' % destination_path + + source_iter = model.get_iter(source_path) + destination_iter = model.get_iter(destination_path) + #model.remove(source_iter) + print source_iter + + print destination_iter + print 'insert' + model.move_after(source_iter, destination_iter) + #self.append_tree(destination_iter) + #~ if self.checkSanity(model, iter_to_copy, target_iter): + #~ self.iterCopy(treeview, model, iter_to_copy, target_iter, pos) + #~ drag_context.finish(gtk.TRUE, gtk.TRUE, eventtime) + #~ treeview.expand_all() + #~ else: + #~ drag_context.finish(gtk.FALSE, gtk.FALSE, eventtime) + + def populate(self): + self.treemodel.clear() + #populate the treeview with a largish tree + for item1 in range(0, 5): + iter_level_1 = self.append_tree('Item ' + str(item1)) + for item2 in range(0, 5): + iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1) + for item3 in range(0, 5): + self.append_tree('Sub Sub Item ' + str(item3), iter_level_2) + + def append_tree(self, name, parent=None): + """ + append to the treeview if parent is null append to root level. + if parent is a valid iter (possibly returned from previous append) then append under the parent + """ + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def menu(self): + """ + popover menu shown on right clicking a treeview item. + """ + self.treeview_menu = Gtk.Menu() + for item in range(0, 5): + menu_item = Gtk.MenuItem("Menu " + str(item)) + self.treeview_menu.append(menu_item) + + def mouse_click(self, tv, event): + if event.button == 3: + # right mouse button pressed popup the menu + self.treeview_menu.show_all() + self.treeview_menu.popup(None, None, None, None, 1, 0) + + def selection(self, tv, treepath, tvcolumn): + """ + on double click get the value of the item we clicked + """ + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + self.entry.set_text(self.selected) + + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut10.glade b/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut10.glade new file mode 100755 index 0000000..49aa625 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut10.glade @@ -0,0 +1,89 @@ + + + + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + + + + + + + + Row1 + + + Row2 + + + Row3 + + + + + False + + + True + False + vertical + + + True + False + + + True + False + True + True + liststore1 + + + + + + 0 + 0 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + alpha + + + False + True + 1 + + + + + + + True + False + + diff --git a/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut18-treeview-treestore-drag.py b/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut18-treeview-treestore-drag.py new file mode 100755 index 0000000..85b6ce0 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tut18-wip/tut18-treeview-treestore-drag.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# [SNIPPET_NAME: gtk3 treeview using a treestore] +# [SNIPPET_CATEGORIES: gtk3] +# [SNIPPET_TAGS: widgets, gtk3] +# [SNIPPET_DESCRIPTION: GTK3 treeview in treestore, show the data in a expandable hierarchy] +# [SNIPPET_AUTHOR: Oliver Marks ] +# [SNIPPET_LICENSE: GPL] +from gi.repository import Gdk, Gtk, GLib + +#http://jason.zwolak.org/technoblog/2010/08/drag-and-drop-with-gtk2-and-perl/ +class application_gui: + """Tutorial 10 text input, display a treeview in expandable tree format.""" + count = 0 + + def __init__(self): + #load in our glade interface + xml = Gtk.Builder() + xml.add_from_file('tut18.glade') + + #grab our widget using get_object this is the name of the widget from glade, window1 is the default name + self.window = xml.get_object('window1') + self.text = xml.get_object('entry1') + + #load our widgets from the glade file + self.widgets = {} + self.widgets['treeview'] = xml.get_object('treeview1') + treeview(self.widgets['treeview'], self.text) + + + #connect to events, in this instance just quit our application + self.window.connect('delete_event', Gtk.main_quit) + self.window.connect('destroy', lambda quit: Gtk.main_quit()) + + #show the window else there is nothing to see :) + self.window.show() + + +class treeview: + treeview = None + treemodel = None + + selected = 'workspace' + + def __init__(self, treeview, entry): + self.entry = entry + self.treeview = treeview + + self.treeview.connect('row-activated', self.selection) + self.treeview.connect('button_press_event', self.mouse_click) + + #self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE) + self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.COPY) + self.treeview.enable_model_drag_dest([("example", 0, 0)], Gdk.DragAction.COPY) + #treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.ACTION_COPY) + #treeview.enable_model_drag_dest([("example", 0, 0)], gtk.gdk.ACTION_COPY) + + self.treeview.connect("drag_data_received", self.onDragDataReceived) + + + #create a storage model in this case a treemodel + self.treemodel = Gtk.TreeStore(str) + self.treeview.set_model(self.treemodel) + + #add columns usually only one in case of the treeview + column = Gtk.TreeViewColumn("Objects") + self.treeview.append_column(column) + + #add in a text renderer so we can see the items we add + cell = Gtk.CellRendererText() + column.pack_start(cell, False) + column.add_attribute(cell, "text", 0) + + self.populate() + self.menu() + + #~ def iterCopy(self, treeview, model, iter_to_copy, target_iter, pos): + #~ + #~ data_column_0 = model.get_value(iter_to_copy, 0) + #~ data_column_1 = model.get_value(iter_to_copy, 1) + #~ if (pos == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) or (pos == gtk.TREE_VIEW_DROP_INTO_OR_AFTER): + #~ new_iter = model.prepend(target_iter, None) + #~ elif pos == gtk.TREE_VIEW_DROP_BEFORE: + #~ new_iter = model.insert_before(None, target_iter) + #~ elif pos == gtk.TREE_VIEW_DROP_AFTER: + #~ new_iter = model.insert_after(None, target_iter) + #~ model.set_value(new_iter, 0, data_column_0) + #~ model.set_value(new_iter, 1, data_column_1) + #~ if model.iter_has_child(iter_to_copy): + #~ for i in range(0, model.iter_n_children(iter_to_copy)): + #~ next_iter_to_copy = model.iter_nth_child(iter_to_copy, i) + #~ self.iterCopy(treeview, model, next_iter_to_copy, new_iter, gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) + + def walk(self, treeiter): + if model.iter_has_child(treeiter): + for i in range(0, model.iter_n_children(treeiter)): + yield model.iter_nth_child(treeiter, i) + + def onDragDataReceived(self, treeview, drag_context, x, y, selection, info, eventtime): + """on data recieve we remove the item and insert in the new location, the insert location must have the same depth""" + model, iter_source = treeview.get_selection().get_selected() + source_path = model.get_path(iter_source) + print 'source iter path %s' % source_path + destination_path, pos = treeview.get_dest_row_at_pos(x, y) + print 'destination iter path %s' % destination_path + + source_iter = model.get_iter(source_path) + destination_iter = model.get_iter(destination_path) + #model.remove(source_iter) + print source_iter + + print destination_iter + print 'insert' + model.move_after(source_iter, destination_iter) + #self.append_tree(destination_iter) + #~ if self.checkSanity(model, iter_to_copy, target_iter): + #~ self.iterCopy(treeview, model, iter_to_copy, target_iter, pos) + #~ drag_context.finish(gtk.TRUE, gtk.TRUE, eventtime) + #~ treeview.expand_all() + #~ else: + #~ drag_context.finish(gtk.FALSE, gtk.FALSE, eventtime) + + def populate(self): + self.treemodel.clear() + #populate the treeview with a largish tree + for item1 in range(0, 5): + iter_level_1 = self.append_tree('Item ' + str(item1)) + for item2 in range(0, 5): + iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1) + for item3 in range(0, 5): + self.append_tree('Sub Sub Item ' + str(item3), iter_level_2) + + def append_tree(self, name, parent=None): + """ + append to the treeview if parent is null append to root level. + if parent is a valid iter (possibly returned from previous append) then append under the parent + """ + myiter = self.treemodel.insert_after(parent, None) + self.treemodel.set_value(myiter, 0, name) + return myiter + + def menu(self): + """ + popover menu shown on right clicking a treeview item. + """ + self.treeview_menu = Gtk.Menu() + for item in range(0, 5): + menu_item = Gtk.MenuItem("Menu " + str(item)) + self.treeview_menu.append(menu_item) + + def mouse_click(self, tv, event): + if event.button == 3: + # right mouse button pressed popup the menu + self.treeview_menu.show_all() + self.treeview_menu.popup(None, None, None, None, 1, 0) + + def selection(self, tv, treepath, tvcolumn): + """ + on double click get the value of the item we clicked + """ + model = tv.get_model() + treeiter = model.get_iter(treepath) + self.selected = model.get_value(treeiter, 0) + self.entry.set_text(self.selected) + + + +application = application_gui() +Gtk.main() diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial1.py b/bases/do-blog/resources/documents/python/gtk3/tutorial1.py new file mode 100755 index 0000000..93c53a8 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial1.py @@ -0,0 +1,44 @@ +#+BEGIN_COMMENT +.. title: Display a GTK window +.. slug: displaying a gtk window +.. date: 2014-01-05 12:00:00 UTC +.. tags: GTK-3, python, window +.. category: python +.. description: GTK Example on loading a window from a glade fiel and displaying it on the screen. +.. type: text +#+END_COMMENT + + +#append this to module path so we can load from the parent directory +import os +import sys +from scaffold import web +from scaffold.loaders import load + + +class page: + name = __name__ + title = 'Display a GTK3 window' + tags = 'GTK3', 'Python', 'window' + intro = 'GTK Example on loading a window from a glade fiel and displaying it on the screen.' + date = '2014-01-05' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut01-windows.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.paragraph.render() + ).append( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """Simple GTK application load a window from a glade file and exit on hitting the window close button, we use the get_object method to get the window by name then use connect to attach to the close and destroy events.""") + web.paragraph.append('There are lots of event we can connect to like mouse click key press and window minimise maximise check the gtk docs or glade for a list of possible event for each widget.') + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut01/tut01-windows.py'))) + web.page.section(web.pre.render()) diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial10.py b/bases/do-blog/resources/documents/python/gtk3/tutorial10.py new file mode 100755 index 0000000..cbaf76d --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial10.py @@ -0,0 +1,37 @@ +#+BEGIN_COMMENT +.. title: GTK-3 Treeview in treestore mode +.. slug: 10-treeview-treestore-mode +.. date: 2014-04-14 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a glade file displaying a collection of values in a treeview using the treestore hierarchy. +.. type: text +#+END_COMMENT + +#+CAPTION: Treeview in tree mode +[[../../../images/gtk/tut10-treeview-treestore.png]] + + +class page: + name = __name__ + title = 'GTK3 Treeview in treestore mode' + tags = 'GTK3', 'Python', 'treestore', 'treeview' + intro = '' + date = '2014-04-14' + path = os.path.abspath('./') + image = web.template.path_image + 'gtk/tut10-treeview-treestore.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create("""The code below will populate the treeview widget with a tree like structure which you can expand and collapse, double clicking will retrieve the selected node in the tree. """) + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut10/tut10-treeview-treestore.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial11.py b/bases/do-blog/resources/documents/python/gtk3/tutorial11.py new file mode 100755 index 0000000..6bcdefc --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial11.py @@ -0,0 +1,31 @@ +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 textview interaction' + tags = 'GTK3', 'Python', 'textview', 'interactive', 'cairo' + intro = 'Load a glade file displaying a textview widget which runs python code on carriage return.' + date = '2014-04-18' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut11-interactive-python-textview.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create("""This example shows using the text view widget to get text entered by a use, it makes use of + marked text blocks so that only some text can be edited and will dynamically read the current line and run it in python interactively. + You can type app.test() or app.show_drawing(True) as examples of the interaction between the widget and python.""") + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut11/tut11-interactive-python-textview.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial12.py b/bases/do-blog/resources/documents/python/gtk3/tutorial12.py new file mode 100755 index 0000000..ee03c3b --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial12.py @@ -0,0 +1,33 @@ +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 Displaying OpenGL inside a drawing area' + tags = 'GTK3', 'Python', 'opengl', 'drawingarea' + intro = 'Load a glade file displaying a drawing area widget and setup a context for opengl.' + date = '2014-04-20' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut12-mixing-opengl-with-gtk.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """Example of rendering opengl inside a gtk drawing area. I have seperated out the opengl code from the main gtk widget this makes it simpler to use in your own applications. """) + + + web.paragraph.append('The example below will draw a basic opengl triangle so you know everything is setup and working, it also attaches to the drawing area resize events so you can resize the window.') + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut12/tut12-mixing-opengl-with-gtk.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial13.py b/bases/do-blog/resources/documents/python/gtk3/tutorial13.py new file mode 100755 index 0000000..c03034e --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial13.py @@ -0,0 +1,20 @@ +#+BEGIN_COMMENT +.. title: GTK-3 Example downloader using a listbox +.. slug: 13-example-list-box-downloader +.. date: 2014-05-01 12:00:00 UTC +.. tags: GTK-3, python, widgets, application +.. category: python +.. description: Populate a listbox with custom widgets, in this case an example file downloader could be the start of a plugin installer. +.. type: text +#+END_COMMENT + +#+CAPTION: File downloader +[[../../../images/gtk/tut12-listbox.png]] + + +A slightly more complex example, loading our gui from a glade file and dynamically add and remove widgets based on an xml file. + +This is the start of a simple file downloader, it reads an xml file and creates a gui dynamically with download buttons for each element in the xml data to retrieve files. + + web.pre.create(load(os.path.abspath('./content/gtk3/tut13/tut13-listbox.py'))) + web.page.section(web.pre.render()) diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial14.py b/bases/do-blog/resources/documents/python/gtk3/tutorial14.py new file mode 100755 index 0000000..a117c8a --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial14.py @@ -0,0 +1,32 @@ +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 Touchscreen events' + tags = 'GTK3', 'Python', 'listbox', 'drawingarea', 'touch' + intro = 'Example of catching touch screen events and filtering device type, helpful for making touch friendly gtk apps.' + date = '2014-05-20' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut14-touchscreen-events.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """The example below demonstrates filtering events for a specific device, in this case a touch screen we then capture touch begin update and end events. + To my knowledge gestures are not supports so you will need to count the begins to determine the number of fingers and handle the updates to determine whats happening""") + + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut14/tut14-touchscreen-events.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial15.py b/bases/do-blog/resources/documents/python/gtk3/tutorial15.py new file mode 100755 index 0000000..a37b1c2 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial15.py @@ -0,0 +1,36 @@ +#+BEGIN_COMMENT +.. title: GTK-3 Simple opengl app with touch events +.. slug: 15-gtk3-opengl-touch-events +.. date: 2014-06-10 12:00:00 UTC +.. tags: GTK-3, python, widgets, touchscreen, opengl +.. category: python +.. description: Example application mixing gtk, opengl and touch, written as a demo but also as an opengl testing enviroment. +.. type: text +#+END_COMMENT + +#+CAPTION: Opengl touch events +[[../../../images/gtk/tut14-touchscreen-events.png]] + + + + """This is a much more complicated example which mashes some of the previous examples together, I have written it as an example but also to test out shaders and different pipelines to get better opengl performance.""") + web.page.section(web.paragraph.render()) + + web.paragraph.create( + """Hopefully this example will help you get started writing awesome touch based apps with gtk, if anything is unclear let me know in the comments below.""") + web.page.section(web.paragraph.render()) + + web.link.create('Launchpad','OpenGL in a drawing area','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/') + web.link.create('Launchpad','Touch screen events','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/') + + + web.pre.create(load(os.path.abspath('./content/gtk3/tut15/tut15-opengl-touch-app-01.py'))) + web.page.section(web.pre.render()) + + web.paragraph.create( + """The file below is a simple helper to reduce the size of the files, it contains the shader and a point and cube class used in the main program above.""") + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut15/helper.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial16.py b/bases/do-blog/resources/documents/python/gtk3/tutorial16.py new file mode 100755 index 0000000..e18bde0 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial16.py @@ -0,0 +1,52 @@ +#+BEGIN_COMMENT +.. title: GTK-3 custom signals example +.. slug: 16-gtk3-custom-signals-example +.. date: 2014-09-11 12:00:00 UTC +.. tags: GTK-3, python, widgets, signals +.. category: python +.. description: Simple example on how to setup, connect to and trigger signals. +.. type: text +#+END_COMMENT + + +#+CAPTION: Opengl touch events +[[../../../images/gtk/tut16-signals.png]] + + +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name = __name__ + title = 'GTK3 custom signals example' + tags = 'GTK3', 'Python', 'signals' + intro = 'Simple example on how to setup, connect to and trigger signals.' + date = '2014-09-11' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut16-signals.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """In this program we create 4 custom signals, the first to are connected to and triggered on launch, the latter two are triggered when you click the buttons.""") + web.page.section(web.paragraph.render()) + + web.paragraph.create( + """We can create new signals using GObject.signal_new, we can then connect the new signal and callback as we normally would using widget .connect methods as normal. + To trigger our custom event we use the .emit() method, this method also aalows us to fake events, for example we might want to fake the user clicking a button.""") + web.page.section(web.paragraph.render()) + + web.link.create('Launchpad','Gtk signals','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/') + + print self.files + web.pre.create(load(self.files + '/gtk3/tut16/tut16-signals.py')) + web.page.section(web.pre.render()) diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial2.py b/bases/do-blog/resources/documents/python/gtk3/tutorial2.py new file mode 100755 index 0000000..a912880 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial2.py @@ -0,0 +1,39 @@ +#+BEGIN_COMMENT +.. title: Adding GTK-3 buttons and switches +.. slug: 02-adding-buttons-and-switches +.. date: 2014-01-08 12:00:00 UTC +.. tags: GTK-3, python, glade +.. category: python +.. description: GTK Example on click and toggle buttons connect to there events to run code when activated by the user. +.. type: text +#+END_COMMENT + + +#+CAPTION: Display GTK-3 Buttons and switches +[[../../../images/gtk/tut02-buttons.png]] + + +class page: + name=__name__ + title = '' + tags = 'GTK3', 'Python', 'window', 'button', 'toggle' + intro = 'GTK Example on click and toggle buttons connect to there events to run code when activated by the user.' + date = '2014-01-08' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut02-buttons.png' + def create_content(self): + + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """The example below loads 4 buttons from a glade 2 standard and 2 toggle buttons, it then connects event handles to show some text when clicked or to show the toggle state if a toggle button was clicked.""") + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut02/tut02-buttons.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial3.py b/bases/do-blog/resources/documents/python/gtk3/tutorial3.py new file mode 100755 index 0000000..e9a6493 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial3.py @@ -0,0 +1,21 @@ +#+BEGIN_COMMENT +.. title: Adding switch and radio widgets +.. slug: 03-adding-radio-buttons-and-switches +.. date: 2014-01-10 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: GTK example demonstrating the use of switches and radio buttons. +.. type: text +#+END_COMMENT + + +#+CAPTION: Adding radio buttons +[[../../../images/gtk/tut03-switch-radio.png]] + + +The below sample shows loading and retrieving state values for radio buttons and switches. + + + + web.pre.create(load(os.path.abspath('./content/gtk3/tut03/tut03-switch-radio.py'))) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial4.py b/bases/do-blog/resources/documents/python/gtk3/tutorial4.py new file mode 100755 index 0000000..9469e8a --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial4.py @@ -0,0 +1,42 @@ +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 Dropdowns and spin buttons' + tags = 'GTK3', 'Python', 'window', 'dropdown', 'select', 'spin' + intro = 'GTK example of some combo / slecet drop down boxesfor user interaction.' + date = '2014-01-15' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut04-text-dropdown-spin.png' + + def create_content(self): + + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """This example demonstrates using some drop down boxes and adding new items and retrieving the selected values. + This snippet also demonstrates the use of spin buttons for selecting a value from a range.""") + web.page.section(web.paragraph.render()) + + web.paragraph.create( + """When creating a combobbox in glade make sure you add a liststore to the widget, and also edit the combobox properties in a seperate window so you can access and the hierarchy menu and assign a cell render to the column in your listview.""") + web.page.section(web.paragraph.render()) + + web.paragraph.create(web.images.create(web.template.path_image + 'gtk/tut04/add-list-store.png', 'Windows 8.1 partition screenshot').render()) + web.paragraph.append(web.images.create(web.template.path_image + 'gtk/tut04/attach-cell-renderer-set-text-column.png', 'Windows 8.1 partition screenshot').render()) + web.paragraph.append(web.images.create(web.template.path_image + 'gtk/tut04/attach-liststore-model.png', 'Windows 8.1 partition screenshot').render()) + web.paragraph.append(web.images.create(web.template.path_image + 'gtk/tut04/edit-combobox-liststore.png', 'Windows 8.1 partition screenshot').render()) + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut04/tut04-text-dropdown-spin.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial5.py b/bases/do-blog/resources/documents/python/gtk3/tutorial5.py new file mode 100755 index 0000000..982985a --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial5.py @@ -0,0 +1,43 @@ +#+BEGIN_COMMENT +.. title: Adding progress bars & spinners +.. slug: 05-adding-progress-bars-and-spinners +.. date: 2014-01-20 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: GTK example of some combo / slecet drop down boxesfor user interaction. +.. type: text +#+END_COMMENT + + +#+CAPTION: Adding dropdown & spin widgets +[[../../../images/gtk/tut04-text-dropdown-spin.png]] + +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 progress bar and spinner' + tags = 'GTK3', 'Python', 'progress', 'spinner' + intro = 'Load a progress bar widget and spinner to demonstrate updating and retriving values.' + date = '2014-01-20' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut05-progress-spinners.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + web.paragraph.create( + """This sample demonstrates two progress bar styles and how to update them too show progress, it also demonstrates starting and stopping a spinning graphic.""") + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut05/tut05-progress-spinners.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial6.py b/bases/do-blog/resources/documents/python/gtk3/tutorial6.py new file mode 100755 index 0000000..c30a2b5 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial6.py @@ -0,0 +1,46 @@ +#+BEGIN_COMMENT +.. title: Displaying dialog boxes +.. slug: 06-dialog-boxes +.. date: 2014-02-03 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Demonstration of opening various dialog boxes and retrieving the selected values. +.. type: text +#+END_COMMENT + + +#+CAPTION: Progress bars and spinners +[[../../../images/gtk/tut06-dialogs.png]] + + +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 Dialog boxes' + tags = 'GTK3', 'Python', 'dialog', 'colour', 'fonts', 'files' + intro = '' + date = '2014-02-03' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut06-dialogs.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """This example program allows you to open the file selector dialogs and displays the selected files, it also demonstrates selecting colour and font from the built in selector dialogs.""") + web.paragraph.append("once a file font or colour has been selected we grab the resulting value and display it as an example of retrieving the value.") + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut06/tut06-dialogs.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial7.py b/bases/do-blog/resources/documents/python/gtk3/tutorial7.py new file mode 100755 index 0000000..2335d15 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial7.py @@ -0,0 +1,45 @@ +#+BEGIN_COMMENT +.. title: App chooer and scale buttons +.. slug: 07-app-chooser-scale-button +.. date: 2014-02-07 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a glade file displaying some scale buttons get value on change, displays some app selector widgets and open the applications. +.. type: text +#+END_COMMENT + + +#+CAPTION: Dialog Boxes +[[../../../images/gtk/tut07-appchooser-scale.png]] + +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 App Chooser and scale buttons' + tags = 'GTK3', 'Python', 'scale' , 'appchooser' + intro = '' + date = '2014-02-07' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut07-appchooser-scale.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """This example program demonstrates the use of appchooser buttons when selecting an application from the drop down launch the application loading a test text file, this could be a video or mp3 or any file type.""") + web.paragraph.append("you can change the application list by modify the content type value in glade this then shows all registered apps for that content type.") + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut07/tut07-appchooser-scale.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial8.py b/bases/do-blog/resources/documents/python/gtk3/tutorial8.py new file mode 100755 index 0000000..23fb8ac --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial8.py @@ -0,0 +1,46 @@ +#+BEGIN_COMMENT +.. title: GTK-3 Calendars and menus +.. slug: 08-calendars-and-menus +.. date: 2014-03-08 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Load a glade file displaying a menu bar and calendar widget. +.. type: text +#+END_COMMENT + +#+CAPTION: App chooser & scale widgets +[[../../../images/gtk/tut08-menu-calendar.png]] + + +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name=__name__ + title = 'GTK3 Calendars and menus' + tags = 'GTK3', 'Python', 'menu', 'calendar' + intro = '' + date = '2014-03-08' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut08-menu-calendar.png' + + def create_content(self): + + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create( + """Simple GTK application demonstrating the use of the date picker and application menus. The menu is hooked upto to show and hide the calendar and to set the text entry field to say hello world. Double clicking a day in the calendar widget will show the date in the text entry field. """) + web.paragraph.append('') + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut08/tut08-menu-calendar.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/gtk3/tutorial9.py b/bases/do-blog/resources/documents/python/gtk3/tutorial9.py new file mode 100755 index 0000000..dc7bae1 --- /dev/null +++ b/bases/do-blog/resources/documents/python/gtk3/tutorial9.py @@ -0,0 +1,42 @@ +#+BEGIN_COMMENT +.. title: GTK-3 Treeview in liststore mode +.. slug: 09-treeview-liststore-mode +.. date: 2014-04-12 12:00:00 UTC +.. tags: GTK-3, python, widgets +.. category: python +.. description: Example of using a treeview, the treeview display can be adjusted using different store models in this case we use the liststore model. +.. type: text +#+END_COMMENT + +#+CAPTION: Calendara & menus +[[../../../images/gtk/tut08-menu-calendar.png]] + +#append this to module path so we can load from the parent directory +import sys, os +from scaffold import web +from scaffold.loaders import load, load_partial + + +class page: + name = __name__ + title = 'GTK3 Treeview in liststore mode' + tags = 'GTK3', 'Python', 'liststore', 'treeview' + intro = 'Example of using a treeview, the treeview display can be adjusted using different store models in this case we use the liststore model.' + date = '2014-04-12' + path = os.path.abspath('./') + image = web.template.path_image + '/gtk/tut09-treeview-listview.png' + + def create_content(self): + web.images.create(self.image, title='GTK Window') + web.page.section( + web.div.create( + web.images.render() + ).set_classes('sources').render() + ) + + web.paragraph.create("The below example demonstrates using the treeview to display table style data, it generates a few rows and shows row selection from a mouse click.") + web.page.section(web.paragraph.render()) + + web.pre.create(load(os.path.abspath('./content/gtk3/tut09/tut09-treeview-listview.py'))) + web.page.section(web.pre.render()) + diff --git a/bases/do-blog/resources/documents/python/kivy/draw-a-cube-while-rendering-widgets.org b/bases/do-blog/resources/documents/python/kivy/draw-a-cube-while-rendering-widgets.org new file mode 100755 index 0000000..bd946eb --- /dev/null +++ b/bases/do-blog/resources/documents/python/kivy/draw-a-cube-while-rendering-widgets.org @@ -0,0 +1,269 @@ +#+TITLE: Drawing a cube while rendering widgets +#+DATE: 2014-08-08 12:00:00 UTC +#+DESCRIPTION: Kivy example on setting up a display and drawing a basic triangle +#+FILETAGS: python:opengl:kivy:glsl +#+CATEGORY: python +#+SLUG: drawing-a-cube-while-rendering-widgets +#+BEGIN_COMMENT +.. title: Drawing a cube while rendering widgets +.. slug: drawing-a-cube-while-rendering-widgets +.. date: 2014-08-08 12:00:00 UTC +.. tags: python, opengl, kivy, glsl +.. category: python +.. description: Kivy example on setting up a display and drawing a basic triangle +.. type: text +#+END_COMMENT + +#+CAPTION: Cube with widgets +[[../../../images/kivy/cube-with-widgets.png]] + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/kivy/'][Example shaded 3D cube]] + + +Now we will expand on the previous example and add a cube class which will give us a cube to work with. + +In this example we use a .kv file and create some widgets we also load a custom widget to load our scene into. + +This should give you a good idea of rendering your own scene and usig kivy widgets to control the scene if you need to later on. + +Pack a load of widgets around our model, we will not do anything with these other than draw them to the screen. + +#+BEGIN_SRC python :tangle kivy-cube-widget-example.py +import kivy +kivy.require('1.0.7') + +from kivy.app import App +from opengl_widget import OpenglWidget + + +class DemoApp(App): + pass + +if __name__ == '__main__': + DemoApp().run() +#+END_SRC + +below is the interface fille that is loaded to pack a load of widgets around our model, we will not do anything with these other than display them. + +#+BEGIN_SRC yaml :tangle kivy-cube-widget-example.kv +#:kivy 1.0 +FloatLayout: + GridLayout: + cols: 1 + row_force_default: False + padding: 5 + BoxLayout: + height: 80 + size_hint_y: None + Button: + text: 'Button 1' + Button: + text: 'Button 2' + BoxLayout: + Accordion: + orientation: 'vertical' + AccordionItem: + title: 'Panel 1' + Button: + text: 'Button 1' + Button: + text: 'Button 2' + Button: + text: 'Button 3' + + AccordionItem: + title: 'Panel 2' + Button: + text: 'Button 4' + Button: + text: 'Button 5' + Button: + text: 'Button 6' + + AccordionItem: + title: 'Panel 3' + Button: + text: 'Button 7' + Button: + text: 'Button 8' + Button: + text: 'Button 9' + OpenglWidget: + width: 200 + height: 200 + TreeView: + label: 'Toolsets' + BoxLayout: + height: 40 + size_hint_y: None + Button: + text: 'Button 1' + Button: + text: 'button 2' +#+END_SRC + +Very simple solid colour shader for our cube. + +#+BEGIN_SRC glsl :tangle kivy-cube-widget-example.glsl +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec4 v_color; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; + +varying vec4 frag_color; + +void main (void) { + vec4 pos = modelview_mat * vec4(v_pos,1.0); + gl_Position = projection_mat * pos; + frag_color = v_color; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 frag_color; +varying vec2 uv_vec; + +uniform sampler2D tex; + +void main (void){ + gl_FragColor = frag_color; +} +#+END_SRC + +This is the meat of the code it creates a custom widget, it gets loaded from the interface file above and then handlers rendering the scene . + +#+BEGIN_SRC python :tangle opengl_widget.py +import os +import sys +from kivy.app import App +from kivy.clock import Clock +from kivy.core.window import Window +from kivy.core.image import Image +from kivy.uix.widget import Widget +from kivy.resources import resource_find +from kivy.graphics.transformation import Matrix +from kivy.graphics.opengl import * +from kivy.graphics import * + +from kivy.uix.widget import Widget +from kivy.graphics import Color, Ellipse + +from numpy import array + +class point: + __slots__ = ['x', 'y', 'z', 'xyz', 'vertex'] + + def __init__(self, p, c=(1, 0, 0)): + """ Position in 3d space as a tuple or list, and colour in tuple or list format""" + self.x, self.y, self.z = p + self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f') + +class cube: + def __init__(self, p1, color, size=0.5): + self.color = array([1, 0, 0], 'f') + self.points = ( + point((p1[0] - size, p1[1] + size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] - size), (color)), + + point((p1[0] - size, p1[1] - size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] - size), (color)), + + ) + + def get_data(self): + return ( + self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, + self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, + + self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, + self.points[0].vertex, self.points[5].vertex, self.points[4].vertex, + + self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, + self.points[0].vertex, self.points[4].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, + self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[1].vertex, self.points[2].vertex, + self.points[6].vertex, self.points[5].vertex, self.points[1].vertex, + + self.points[6].vertex, self.points[4].vertex, self.points[5].vertex, + self.points[6].vertex, self.points[7].vertex, self.points[4].vertex, + ) + + + +class OpenglWidget(Widget): + def __init__(self, **kwargs): + self.canvas = RenderContext(compute_normal_mat=True) + self.canvas.shader.source = resource_find('kivy.glsl') + + self.c = cube((0, 0, 0), (1, 0, 0), 2.0) + self.vertices = [] + for item in self.c.get_data(): + for a in item: + self.vertices.append(a) + self.vertices.append(1) # add alpha + + self.indices = range(0, len(self.vertices)) + + with self.canvas: + self.cb = Callback(self.setup_gl_context) + PushMatrix() + self.setup_scene() + PopMatrix() + self.cb = Callback(self.reset_gl_context) + Clock.schedule_interval(self.update_glsl, 1 / 60.) + + def setup_gl_context(self, *args): + glEnable(GL_DEPTH_TEST) + + def reset_gl_context(self, *args): + glDisable(GL_DEPTH_TEST) + + def update_glsl(self, *largs): + aspect = float(self.height) / float(self.width) + projection_mat = Matrix() + projection_mat.perspective(45.0, aspect, 1.0, 80.0) + model = Matrix().look_at( + 0.0, 0.0, 25.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0) + + self.canvas['projection_mat'] = projection_mat + self.canvas['modelview_mat'] = model + self.rot.angle += 1 + + def setup_scene(self): + Color(0, 0, 0, 1) + PushMatrix() + self.rot = Rotate(1, 0, 1, 0) + + vertex_format = [ + ('v_pos', 3, 'float'), + ('v_color', 4, 'float'), + ] + + UpdateNormalMatrix() + self.mesh = Mesh( + vertices=self.vertices, + indices=self.indices, + fmt=vertex_format, + mode='triangles', + ) + PopMatrix() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/kivy/draw-a-shaded-triangle-with-kivy.org b/bases/do-blog/resources/documents/python/kivy/draw-a-shaded-triangle-with-kivy.org new file mode 100755 index 0000000..35eb0d6 --- /dev/null +++ b/bases/do-blog/resources/documents/python/kivy/draw-a-shaded-triangle-with-kivy.org @@ -0,0 +1,131 @@ +#+TITLE: Draw a simple triangle with kivy +#+DATE: 2014-07-20 12:00:00 UTC +#+DESCRIPTION: Kivy example on setting up and displaying a basic triangle +#+FILETAGS: python:opengl:kivy +#+CATEGORY: python +#+SLUG: draw-a-shaded-triangle-with-kivy +#+BEGIN_COMMENT +.. title: Draw a simple triangle with kivy +.. slug: draw-a-shaded-triangle-with-kivy +.. date: 2014-07-20 12:00:00 UTC +.. tags: python, opengl, kivy +.. category: python +.. description: Kivy example on setting up and displaying a basic triangle +.. type: text +#+END_COMMENT + +#+CAPTION: Kivy shaded triangle +[[../../../images/kivy/triangle.png]] + + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/kivy/kivy_opengl_triangle][Shaded triangle source code]] + +This example will create a simple triangle and render it on screen, opengl in kivy is wrapped so you use generally use helper methods for rendering. + +the main one is meshes for loading in your points and indices and textures to draw your model and rendercontext which activates your shaders. + +#+BEGIN_SRC python :tangle kivy_opengl_triangle.py +from kivy.app import App +from kivy.clock import Clock +from kivy.core.window import Window +from kivy.core.image import Image +from kivy.uix.widget import Widget +from kivy.resources import resource_find +from kivy.graphics.transformation import Matrix +from kivy.graphics.opengl import * +from kivy.graphics import * + + +class Renderer(Widget): + def __init__(self, **kwargs): + self.canvas = RenderContext(compute_normal_mat=True) + self.canvas.shader.source = resource_find('shaders-opengl-triangle.glsl') + + super(Renderer, self).__init__(**kwargs) + with self.canvas: + self.cb = Callback(self.setup_gl_context) + PushMatrix() + self.setup_scene() + PopMatrix() + self.cb = Callback(self.reset_gl_context) + Clock.schedule_interval(self.update_glsl, 1 / 60.) + + def setup_gl_context(self, *args): + glEnable(GL_DEPTH_TEST) + + def reset_gl_context(self, *args): + glDisable(GL_DEPTH_TEST) + + def update_glsl(self, *largs): + proj = Matrix().view_clip(0, self.width, 0, self.height, 1, 100, 0) + self.canvas['projection_mat'] = proj + + def setup_scene(self): + Color(0, 0, 0, 1) + PushMatrix() + + indices = [0, 1, 2, 3, 0, 2] + vertex_format = [ + ('v_pos', 3, 'float'), + ('v_color', 4, 'float'), + ] + vertices = [ + 10.0 , 10.0 , 1.0, 1.0, 1.0, 0.0, 0.0, + 10.0 , 200.0, 1.0, 1.0, 0.0, 1.0, 0.0, + 200.0, 200.0, 1.0, 1.0, 0.0, 0.0, 1.0, + ] + + UpdateNormalMatrix() + self.mesh = Mesh( + vertices=vertices, + indices=indices, + fmt=vertex_format, + mode='triangles', + ) + PopMatrix() + + +class RendererApp(App): + def build(self): + return Renderer() + +if __name__ == "__main__": + RendererApp().run() +#+END_SRC + +#+BEGIN_SRC glsl :tangle shaders-opengl-triangle.glsl +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec4 v_color; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; + +varying vec4 frag_color; + +void main (void) { + vec4 pos = modelview_mat * vec4(v_pos,1.0); + gl_Position = projection_mat * pos; + frag_color = v_color; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 frag_color; +varying vec2 uv_vec; + +uniform sampler2D tex; + +void main (void){ + gl_FragColor = frag_color; +} +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/kivy/draw-a-textured-square-with-kivy.org b/bases/do-blog/resources/documents/python/kivy/draw-a-textured-square-with-kivy.org new file mode 100755 index 0000000..4ed4435 --- /dev/null +++ b/bases/do-blog/resources/documents/python/kivy/draw-a-textured-square-with-kivy.org @@ -0,0 +1,133 @@ +#+TITLE: Draw a textured square with kivy +#+DATE: 2014-08-05 12:00:00 UTC +#+DESCRIPTION: Kivy example drawing a square and loading an image and applying to the quad as a simple texture. +#+FILETAGS: python:opengl:kivy:glsl +#+CATEGORY: python +#+SLUG: draw-a-textured-square-with-kivy +#+BEGIN_COMMENT +.. title: Draw a textured square with kivy +.. slug: draw-a-textured-square-with-kivy +.. date: 2014-08-05 12:00:00 UTC +.. tags: python, opengl, kivy, glsl +.. category: python +.. description: Kivy example drawing a square and loading an image and applying to the quad as a simple texture. +.. type: text +#+END_COMMENT + +#+CAPTION: Kivy textured quad +[[../../../images/kivy/quad-texture.png]] + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/kivy/kivy-opengl-texture/][Textured quad source code]] + +This example expands on the first and simply loads a texture and applys it to two triangles which make up a square. + +#+BEGIN_SRC python :tangle kivy-opengl-texture.py +from kivy.app import App +from kivy.clock import Clock +from kivy.core.window import Window +from kivy.core.image import Image +from kivy.uix.widget import Widget +from kivy.resources import resource_find +from kivy.graphics.transformation import Matrix +from kivy.graphics.opengl import * +from kivy.graphics import * + + +class Renderer(Widget): + def __init__(self, **kwargs): + self.canvas = RenderContext(compute_normal_mat=True) + self._tpath = resource_find('testing.png') + self.canvas.shader.source = resource_find('shaders-opengl-texture.glsl') + + super(Renderer, self).__init__(**kwargs) + with self.canvas: + self.cb = Callback(self.setup_gl_context) + PushMatrix() + self.setup_scene() + PopMatrix() + self.cb = Callback(self.reset_gl_context) + Clock.schedule_interval(self.update_glsl, 1 / 60.) + + def setup_gl_context(self, *args): + glEnable(GL_DEPTH_TEST) + + def reset_gl_context(self, *args): + glDisable(GL_DEPTH_TEST) + + def update_glsl(self, *largs): + proj = Matrix().view_clip(0, self.width, 0, self.height, 1, 100, 0) + self.canvas['projection_mat'] = proj + + def setup_scene(self): + Color(0, 0, 0, 1) + PushMatrix() + + indices = [0, 1, 2, 3, 0, 2] + vertex_format = [ + ('v_pos', 3, 'float'), + ('v_uv', 2, 'float'), + ] + + vertices = [ + 10.0 , 10.0 , 1.0, 0.0, 0.0, + 10.0 , 200.0, 1.0, 0.0, 1.0, + 200.0, 200.0, 1.0, 1.0, 1.0, + 200.0, 10.0 , 1.0, 1.0, 0.0, + ] + + UpdateNormalMatrix() + self.mesh = Mesh( + vertices=vertices, + indices=indices, + fmt=vertex_format, + mode='triangles', + ) + self.mesh.texture = Image(self._tpath).texture + PopMatrix() + + +class RendererApp(App): + def build(self): + return Renderer() + +if __name__ == "__main__": + RendererApp().run() +#+END_SRC + +#+BEGIN_SRC glsl :tangle shaders-opengl-texture.glsl +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec2 v_uv; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; + +varying vec2 uv_vec; + +void main (void) { + vec4 pos = modelview_mat * vec4(v_pos,1.0); + gl_Position = projection_mat * pos; + uv_vec = v_uv; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 frag_color; +varying vec2 uv_vec; + +uniform sampler2D tex; + +void main (void){ + vec4 color = texture2D(tex, uv_vec); + gl_FragColor = color; +} +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/kivy/drawing-two-cubes-with-different-shaders-using-kivy.org b/bases/do-blog/resources/documents/python/kivy/drawing-two-cubes-with-different-shaders-using-kivy.org new file mode 100755 index 0000000..98f2d9d --- /dev/null +++ b/bases/do-blog/resources/documents/python/kivy/drawing-two-cubes-with-different-shaders-using-kivy.org @@ -0,0 +1,316 @@ +#+TITLE: Draw two cubes using Kivy with different shaders. +#+DATE: 2014-09-01 12:00:00 UTC +#+DESCRIPTION: Kivy example draw two cubes with different shaders and vertices so they can be moved seperately. +#+FILETAGS: python:opengl:kivy:glsl +#+CATEGORY: python +#+SLUG: drawing-two-cubes-with-different-shaders-using-kivy +#+BEGIN_COMMENT +.. title: Draw two cubes using Kivy with different shaders. +.. slug: drawing-two-cubes-with-different-shaders-using-kivy +.. date: 2014-09-01 12:00:00 UTC +.. tags: python, opengl, kivy, glsl +.. category: python +.. description: Kivy example draw two cubes with different shaders and vertices so they can be moved seperately. +.. type: text +#+END_COMMENT + +#+CAPTION: Multiple cube meshes +[[../../../images/kivy/multi-mesh.png]] + + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/kivy/][Multiple mesh source code]] + +Expanding on the last example this code demonstrates loading multiple models and using different shaders per model. + +#+BEGIN_SRC python :tangle kivy-multi-mesh-demo.py +import kivy +kivy.require('1.0.7') + +from kivy.app import App +from opengl_widget import OpenglWidget + + +class DemoApp(App): + pass + +if __name__ == '__main__': + DemoApp().run() +#+END_SRC + +#+BEGIN_SRC python :tangle kivy-multi-mesh-demo.kv +#:kivy 1.0 +FloatLayout: + OpenglWidget: +#+END_SRC + +First shader to create a green object. + +#+BEGIN_SRC glsl :tangle kivy-green.glsl +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec4 v_color; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; + +varying vec4 frag_color; + +void main (void) { + vec4 pos = modelview_mat * vec4(v_pos,1.0); + gl_Position = projection_mat * pos; + frag_color = v_color; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 frag_color; +varying vec2 uv_vec; + +uniform sampler2D tex; + +void main (void){ + gl_FragColor = vec4(0, 1, 0, 1); +} +#+END_SRC + +Second shader creates a blue object. + +#+BEGIN_SRC glsl :tangle kivy-blue.glsl +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec4 v_color; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; + +varying vec4 frag_color; + +void main (void) { + vec4 pos = modelview_mat * vec4(v_pos,1.0); + gl_Position = projection_mat * pos; + frag_color = v_color; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 frag_color; +varying vec2 uv_vec; + +uniform sampler2D tex; + +void main (void){ + gl_FragColor = vec4(0, 0, 1, 1); +} +#+END_SRC + + +#+BEGIN_SRC python :tangle opengl_widget.py +import os +import sys +from kivy.app import App +from kivy.clock import Clock +from kivy.core.window import Window +from kivy.core.image import Image +from kivy.uix.widget import Widget +from kivy.resources import resource_find +from kivy.graphics.transformation import Matrix +from kivy.graphics.opengl import * +from kivy.graphics import * + +from kivy.uix.widget import Widget +from kivy.graphics import Color, Ellipse + +from numpy import array + + +#store a single vertex in this class +class point: + def __init__(self, p, c=(1, 0, 0, 1)): + self.x, self.y, self.z = p + self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2], c[3]], 'f') + + +#simple class to create the vertices for a cube for testing. +class cube: + def __init__(self, p1, color=(1, 0, 0, 1), size=0.5): + self.color = array([1, 0, 0, 1], 'f') + self.points = ( + point((p1[0] - size, p1[1] + size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] + size, p1[2] - size), (color)), + + point((p1[0] - size, p1[1] - size, p1[2] - size), (color)), + point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] + size), (color)), + point((p1[0] + size, p1[1] - size, p1[2] - size), (color)), + + ) + + def get_data(self): + return ( + self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, + self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, + + self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, + self.points[0].vertex, self.points[5].vertex, self.points[4].vertex, + + self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, + self.points[0].vertex, self.points[4].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, + self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, + + self.points[6].vertex, self.points[1].vertex, self.points[2].vertex, + self.points[6].vertex, self.points[5].vertex, self.points[1].vertex, + + self.points[6].vertex, self.points[4].vertex, self.points[5].vertex, + self.points[6].vertex, self.points[7].vertex, self.points[4].vertex, + ) + + +#custom widget for render our scene into +class OpenglWidget(Widget): + + def __init__(self, **kwargs): + self.instructions = InstructionGroup() + self.canvas = RenderContext(compute_normal_mat=True) + self.canvas.shader.source = resource_find('kivy.glsl') + + #create first shader for cube 1 + self.cube1 = RenderContext(compute_normal_mat=True) + self.cube1.shader.source = resource_find('kivy-blue.glsl') + + #create first shader for cube 2 + self.cube2 = RenderContext(compute_normal_mat=True) + self.cube2.shader.source = resource_find('kivy-green.glsl') + + #create 2 cubes for testing + cube1 = cube((0, -4, 0), size = 2.0) + cube2 = cube((0, 4, 0), size = 2.0) + + #generate vertex array for cube 1 + self.vertices_cube1 = [] + for item in cube1.get_data(): + for a in item: + self.vertices_cube1.append(a) + + #generate vertex array for cube 2 + self.vertices_cube2 = [] + for item in cube2.get_data(): + for a in item: + self.vertices_cube2.append(a) + + #calcualte indices for both cubes + self.indices_cube1 = range(0, 36) + self.indices_cube2 = range(0, 36) + + #add our render contexts to an instruction group for drawing + self.instructions.add(self.cube1) + self.instructions.add(self.cube2) + self.canvas.add(self.instructions) + with self.canvas: + self.cb = Callback(self.setup_gl_context) + PushMatrix() + self.scene() + PopMatrix() + self.cb = Callback(self.reset_gl_context) + Clock.schedule_interval(self.update_glsl, 1 / 60.) + + #create our scene and update our two models + def scene(self): + Color(0, 0, 0, 1) + with self.cube2: + self.cube1_mesh() + with self.cube1: + self.cube2_mesh() + + def setup_gl_context(self, *args): + glEnable(GL_DEPTH_TEST) + + def reset_gl_context(self, *args): + glDisable(GL_DEPTH_TEST) + + #setup the projection matrices + def update_glsl(self, *largs): + aspect = float(self.width) / float(self.height) + projection_mat = Matrix() + projection_mat.perspective(45.0, aspect, 1.0, 80.0) + model = Matrix().look_at( + 0.0, 0.0, 25.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0) + + self.canvas['projection_mat'] = projection_mat + self.canvas['modelview_mat'] = model + + self.cube1['projection_mat'] = projection_mat + self.cube1['modelview_mat'] = model + + self.cube2['projection_mat'] = projection_mat + self.cube2['modelview_mat'] = model + self.rot_cube1.angle += 1 + self.rot_cube2.angle += 1 + + def cube1_mesh(self): + Color(0, 0, 0, 1) + PushMatrix() + + self.rot_cube1 = Rotate(1, 0, 1, 0) + + vertex_format = [ + ('v_pos', 3, 'float'), + ('v_color', 4, 'float'), + ] + + UpdateNormalMatrix() + self.mesh1 = Mesh( + vertices=self.vertices_cube1, + indices=self.indices_cube1, + fmt=vertex_format, + mode='triangles', + ) + + PopMatrix() + + def cube2_mesh(self): + + PushMatrix() + Color(0, 0, 0, 1) + self.rot_cube2 = Rotate(1, 0, -1, 0) + + vertex_format = [ + ('v_pos', 3, 'float'), + ('v_color', 4, 'float'), + ] + + UpdateNormalMatrix() + self.mesh2 = Mesh( + vertices=self.vertices_cube2, + indices=self.indices_cube2, + fmt=vertex_format, + mode='triangles', + ) + PopMatrix() + + def setup_scene(self): + Color(0, 0, 0, 1) + self.cube1_mesh() + self.cube1_mesh() +#+END_SRC diff --git a/bases/do-blog/resources/documents/python/opengl/pixel-shader-example.org b/bases/do-blog/resources/documents/python/opengl/pixel-shader-example.org new file mode 100755 index 0000000..9076fdf --- /dev/null +++ b/bases/do-blog/resources/documents/python/opengl/pixel-shader-example.org @@ -0,0 +1,432 @@ +#+TITLE: Python pixel shading example using point sprites +#+DATE: 2014-10-08 12:00:00 UTC +#+DESCRIPTION: OpenGL program that does pixel shading, OpenGL pixels with size and textures often used for particle effects. +#+FILETAGS: python:opengl:sprites +#+CATEGORY: python +#+SLUG: pixel-shading-example +#+THUMBNAIL: ../../../images/opengl/point-sprite-shader.png + +#+BEGIN_COMMENT +.. title: Python pixel shading example using point sprites +.. slug: pixel-shading-example +.. date: 2014-10-08 12:00:00 UTC +.. tags: python, opengl, sprites +.. category: python +.. description: OpenGL program that does pixel shading, OpenGL pixels with size and textures often used for particle effects. +.. type: text +#+END_COMMENT + + +#+CAPTION: Point sprite shader +[[../../../images/opengl/point-sprite-shader.png]] + +[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/opengl/][OpenGL pixel shader source code]] + +This example shows rendering textures using GL_POINTS primitives, it should work on most modern hardware I have hit issues on older intel cards which seems to be a driver bug causing nothing to display. If you encouter this try setting the shader code to a solid colour to see if the pixels have size. + +OpenGL provides a point size parameter which we use to give our points size, there is a similar option for lines. This has the benefit of sending less data to the gpu improving efficiency, the other option is to send 4 vertices instead of one and rendering the texture to two triangles. + +We need to enable support for pixel size using glEnable, the appropriate settings are in the helper file and are set when the window is setup. +The main one being GL_POINT_SPRITE and GL_POINT_SPRITE_ARB also glPointSize() if your using a fixed function pipeline instead of shaders, if your using a shader you can set gl_PointSize in your vertex shader. + +#+BEGIN_SRC python :tangle opengl-pixle-shader.py +#!/usr/bin/env python +import os +import sys +import time +import random +import pprint + +import Xlib +from Xlib.display import Display +from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject +from numpy import array + +from OpenGL.GL import * +from OpenGL.GLU import gluPerspective, gluLookAt +from OpenGL.arrays import vbo +from OpenGL import GLX + +from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER +from OpenGL.GL import shaders, glGetUniformLocation + + +from helper import shader, gtkgl + + +class scene: + width, height = 600, 400 + camera_distance = 25 + texture_id = None + + def __init__(self): + """setup everything in the correct order""" + self.glwrap = gtkgl() + self.setup_opengl() + self.generate() + self.gui() + + def gui(self): + """load in the gui and connect the events and set our properties""" + self.start_time = time.time() + self.frame = 1 + xml = Gtk.Builder() + xml.add_from_file('gui.glade') + + self.window = xml.get_object('window1') + self.button = xml.get_object('btngenerate') + + self.canvas_widget = xml.get_object('canvas') + self.canvas_widget.connect('configure_event', self.on_configure_event) + self.canvas_widget.connect('draw', self.on_draw) + self.canvas_widget.set_double_buffered(False) + self.canvas_widget.set_size_request(self.glwrap.width, self.glwrap.height) + + self.button.connect('pressed', self.generate) + + self.window.show_all() + self.setup_shaders() + GObject.idle_add(self.loop_draw) + + + def loop_draw(self): + #send redraw event to drawing area widget + self.canvas_widget.queue_draw() + return True + + def on_configure_event(self, widget, event): + """if we recieve a configure event for example a resize, then grab the context settings and resize our scene """ + self.glwrap.width = widget.get_allocation().width + self.glwrap.height = widget.get_allocation().height + self.width, self.height = self.glwrap.width, self.glwrap.height + + #update our states because we have reconfigured the display + self.glwrap.configure(widget.get_window()) + self.glwrap.draw_start() + self.update_camera() + + + glEnable(GL_TEXTURE_2D) + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDepthFunc(GL_LEQUAL) + glDepthRange(0.0, 1.0) + glEnable(GL_CULL_FACE) + glCullFace(GL_BACK) + glFrontFace(GL_CW) + + self.glwrap.draw_finish() + return True + + def on_draw(self, widget, context): + """if we recieve a draw event redraw our opengl scene""" + self.elapsed_time = time.time() - self.start_time + self.frame += 1 + + if self.elapsed_time > 1: + print('fps %d' % self.frame) + self.start_time = time.time() + self.frame = 1 + self.glwrap.draw_start() + #self.draw() + glClearDepth(1.0) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + self.draw_shader_points_texture() + self.glwrap.draw_finish() + + def generate(self, *args): + """randomly position a few textured points""" + self.point_sprites = [] + for i in range(0, 10): + self.point_sprites.append((random.uniform(-8, 8), random.uniform(-8, 8), random.uniform(-8, 8))) + + print('Generated %s points' % str(len(self.point_sprites))) + self.vertex_vbo = vbo.VBO(array(self.point_sprites, 'f')) + + def setup_shaders(self): + self.shader_program = shader() + self.shader_program.compile() + self.texture_id = self.shader_program.load_image('testing.png') + + def setup_opengl(self): + glShadeModel(GL_SMOOTH) + glClearColor(0.0, 0.0, 0.0, 0.0) + glClearDepth(1.0) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + + def update_camera(self): + """Setup a very basic camera""" + glViewport(0, 0, self.width, self.height) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluPerspective(45, 1.0 * self.width / self.height, 1.0, 80.0) + gluLookAt(self.camera_distance, self.camera_distance, self.camera_distance, # location + 0.0, 0.0, 0.0, # lookat + 0.0, 1.0, 0.0) # up direction + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + + def draw_shader_points_texture(self): + glEnableClientState(GL_VERTEX_ARRAY) + glEnableClientState(GL_COLOR_ARRAY) + glEnableClientState(GL_TEXTURE_COORD_ARRAY) + matrix_model_view = glGetFloatv(GL_MODELVIEW_MATRIX) + matrix_projection = glGetFloatv(GL_PROJECTION_MATRIX) + + glUseProgram(self.shader_program.program) + self.vertex_vbo.bind() + glEnableVertexAttribArray(self.shader_program.point_vertex) + glVertexAttribPointer(self.shader_program.point_vertex, 3, GL_FLOAT, GL_FALSE, 12, self.vertex_vbo) + + #send the model and projection matrices to the shader + glUniformMatrix4fv(self.shader_program.point_matrix_model_view, 1, GL_FALSE, matrix_model_view) + glUniformMatrix4fv(self.shader_program.point_matrix_projection, 1, GL_FALSE, matrix_projection) + + #make the texture we loaded in on shader initalisation active, passing the texture id supplied a t this + glActiveTexture(GL_TEXTURE0) + glBindTexture(GL_TEXTURE_2D, self.texture_id) + glUniform1i(self.shader_program.texture_uniform, 0) + + glDrawArrays(GL_POINTS, 0, len(self.vertex_vbo)) + + glDisableVertexAttribArray(self.shader_program.point_vertex) + self.vertex_vbo.unbind() + glDisableClientState(GL_COLOR_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + glDisableClientState(GL_TEXTURE_COORD_ARRAY) + + def draw(self): + glEnable(GL_DEPTH_TEST) + glClearDepth(1.0) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + self.draw_shader_points_texture() + + +if __name__ == '__main__': + glexample = scene() + Gtk.main() +#+END_SRC + +This file holds various helper code mainly to load and setup the shaders, it also wraps the compile shader function to fix a bug in the packaged version of pyopengl. +This help file also handles loading the texture for the sprites and seting up gtk context for rendering the scene to. + +#+BEGIN_SRC python :tangle helper.py +import os +import sys +import numpy +from numpy import array +from ctypes import * +from OpenGL.GL import * +from OpenGL import GLX +from OpenGL.GL import shaders +from OpenGL.arrays import vbo +from OpenGL.raw.GL.ARB.point_sprite import GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB +from OpenGL._bytes import bytes, _NULL_8_BYTE + +import Xlib +from Xlib.display import Display +try: + from OpenGL.GLX import struct__XDisplay +except ImportError as err: + from OpenGL.raw._GLX import struct__XDisplay +from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject + +from PIL import Image + +def glDebug(): + error = glGetError() + if error: + print ('opengl error ' + str(error)) + return True + return False + + +class gtkgl: + """ wrapper to enable opengl in our gtk application + useful link http://www.opengl.org/wiki/Programming_OpenGL_in_Linux:_GLX_and_Xlib""" + #these method do not seem to exist in python x11 library lets exploit the c methods + xlib = cdll.LoadLibrary('libX11.so') + xlib.XOpenDisplay.argtypes = [c_char_p] + xlib.XOpenDisplay.restype = POINTER(struct__XDisplay) + xdisplay = xlib.XOpenDisplay(None) + display = Xlib.display.Display() + attrs = [] + + xwindow_id = None + width, height = 500,300 + + def __init__(self): + """ lets setup are opengl settings and create the context for our window """ + self.add_attribute(GLX.GLX_RGBA, True) + self.add_attribute(GLX.GLX_RED_SIZE, 8) + self.add_attribute(GLX.GLX_GREEN_SIZE, 8) + self.add_attribute(GLX.GLX_BLUE_SIZE, 8) + self.add_attribute(GLX.GLX_DOUBLEBUFFER, 1) + self.add_attribute(GLX.GLX_DEPTH_SIZE, 24) + + xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes()) + print("run glxinfo to match this visual id %s " % hex(xvinfo.contents.visualid)) + self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True) + + def add_attribute(self, setting, value): + """just to nicely add opengl parameters""" + self.attrs.append(setting) + self.attrs.append(value) + + def get_attributes(self): + """ return our parameters in the expected structure""" + attrs = self.attrs + [0, 0] + return (c_int * len(attrs))(*attrs) + + def configure(self, wid): + """ """ + self.xwindow_id = GdkX11.X11Window.get_xid(wid) + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print('failed configuring context') + glViewport(0, 0, self.width, self.height) + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_DEPTH_TEST) + glEnable(GL_BLEND) + + #settings related to enbling pixel sprites. + glEnable(GL_POINT_SPRITE) + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE) + glEnable(GL_PROGRAM_POINT_SIZE) + glEnable(GL_POINT_SPRITE_ARB) + + #glPointSize(16) + + glShadeModel(GL_SMOOTH) + glDepthFunc(GL_LEQUAL) + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + + def draw_start(self): + """make cairo context current for drawing""" + if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)): + print ("failed to get the context for drawing") + + def draw_finish(self): + """swap buffer when we have finished drawing""" + GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id) + + +def compileShader( source, shaderType ): + + """Compile shader source of given type + source -- GLSL source-code for the shader + shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc, + returns GLuint compiled shader reference + raises RuntimeError when a compilation failure occurs + """ + if isinstance(source, str): + + source = [source] + elif isinstance(source, bytes): + + source = [source.decode('utf-8')] + + shader = glCreateShader(shaderType) + glShaderSource(shader, source) + glCompileShader(shader) + result = glGetShaderiv(shader, GL_COMPILE_STATUS) + + if not(result): + # TODO: this will be wrong if the user has + # disabled traditional unpacking array support. + raise RuntimeError( + """Shader compile failure (%s): %s"""%( + result, + glGetShaderInfoLog( shader ), + ), + source, + shaderType, + ) + return shader + + +class shader: + vertex = """#version 120 + //attributes in values + attribute vec3 vertex_pos; + + uniform mat4 modelview_mat; + uniform mat4 projection_mat; + + void main(){ + vec4 pos = modelview_mat * vec4(vertex_pos, 1.0); + gl_Position = projection_mat * pos; + gl_PointSize = 16.0; + }""" + + fragment = """#version 120 + uniform sampler2D quad_texture; + void main(){ + gl_FragColor = texture2D(quad_texture, gl_PointCoord); + //gl_FragColor = vec4(gl_TexCoord[0].st, 1, 0.75);//for debug + //gl_FragColor = vec4(gl_PointCoord, 1, 0.75);//for debug + }""" + + program = None + + def load_image(self, filename): + """load our image using pil and setup and make it available to opengl""" + path = os.path.abspath(filename) + im = Image.open(path) + try: + + ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBA", 0, -1) + except SystemError as error: + ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBX", 0, -1) + except: + return None + + texture_id = glGenTextures(1) + glDebug() + glBindTexture(GL_TEXTURE_2D, texture_id) + glDebug() + glPixelStorei(GL_UNPACK_ALIGNMENT, 1) + glDebug() + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image) + glDebug() + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + glDebug() + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER) + glDebug() + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER) + glDebug() + + if glGetError(): + print(glGetError()) + + return texture_id + + def compile(self): + """setup our shaders and compile for rendering our primitives""" + self.program = shaders.compileProgram( + compileShader(self.vertex, GL_VERTEX_SHADER), + compileShader(self.fragment, GL_FRAGMENT_SHADER),) + + self.point_vertex = glGetAttribLocation(self.program, b'vertex_pos') + self.texture_uniform = glGetUniformLocation(self.program, b'quad_texture') + self.point_matrix_model_view = glGetUniformLocation(self.program, b"modelview_mat") + self.point_matrix_projection = glGetUniformLocation(self.program, b"projection_mat") + self.fixed_quad_tex_coords = [[[0.0, 1.0]], [[1.0, 1.0]], [[0.0, 0.0]], [[1.0, 0.0]]] + self.fixed_quad_indices = [0, 1, 2, 1, 2, 3] + self.fixed_quad_indices_vbo = vbo.VBO( + array([self.fixed_quad_indices], dtype='uint32'), target=GL_ELEMENT_ARRAY_BUFFER) + +class point: + __slots__ = ['x', 'y', 'z', 'xyz', 'vertex'] + + def __init__(self, p, c=(1, 0, 0)): + """ Position in 3d space as a tuple or list, and colour in tuple or list format""" + self.x, self.y, self.z = p + self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f') +#+END_SRC + diff --git a/bases/do-blog/resources/documents/python/xorg/switch-window-alt-tab-style-with-python.org b/bases/do-blog/resources/documents/python/xorg/switch-window-alt-tab-style-with-python.org new file mode 100755 index 0000000..48831d3 --- /dev/null +++ b/bases/do-blog/resources/documents/python/xorg/switch-window-alt-tab-style-with-python.org @@ -0,0 +1,142 @@ +#+TITLE: Demonstration on how to switch windows with python +#+DATE: 2017-04-07 12:00:00 UTC +#+DESCRIPTION: Shows how to get all window and change focus sequentially, could be used to implement your own window switcher. +#+FILETAGS: xcb:python:xorg +#+CATEGORY: python +#+SLUG: xcb-window-switching-example +#+THUMBNAIL: ../../../images/xcb/xcb-window-switcher.jpg +#+BEGIN_COMMENT +.. title: Demonstration on how to switch windows with python +.. slug: xcb-window-switching-example +.. date: 2017-04-07 12:00:00 UTC +.. tags: xcb, python, xorg +.. category: python +.. description: Shows how to get all window and change focus sequentially, could be used to implement your own window switcher. +.. type: text +#+END_COMMENT + + +#+CAPTION: Window list output +[[../../../images/xcb/xcb-window-switcher.jpg]] + + +I have been experimenting with writing my own launcher app similar to gnome do, one of the things i wanted was to +switch windows by typing the name, so i wrote the below example to work out how to switch window. + +It works with you xserver to request a list of windows and then send the event to change focus, i have only tested +this in gnome how ever it will likely work with other desktops. + +The main thing you need todo is to set input focus before raising the window, if you do not some window managers like +gnome will just notify you instead of raising the window. + + +#+BEGIN_SRC python +#!/usr/bin/env python +import time +import struct + +import xcb +from xcb import xproto + +# standard xcb to get an xserver connection and root window +xserver = xcb.connect() +xclient = xserver.core +setup = xserver.get_setup() + +canvas = setup.roots[0] +root_window = canvas.root + +unsigned_int_32_max_size = (1 << 32) - 1 + + +def get_atom(name): + return xclient.InternAtom( + 0, + len(name), + name + ).reply() + + +# setup a load of atoms as constants for later use +_NET_WM_STATE = get_atom('_NET_WM_STATE') +_NET_WM_NAME = get_atom('_NET_WM_NAME') +_NET_WM_WINDOW_TYPE = get_atom('_NET_WM_WINDOW_TYPE') +_NET_WM_WINDOW_TYPE_APPLICATION = get_atom('_NET_WM_WINDOW_TYPE_NORMAL') +_NET_CLIENT_LIST = get_atom('_NET_CLIENT_LIST') + +# query _NET_CLIENT_LIST which contains a list of windows +clientlist_reply = xclient.GetProperty( + False, root_window, _NET_CLIENT_LIST.atom, + xcb.xproto.GetPropertyType.Any, + 0, unsigned_int_32_max_size).reply() + +# unpack the reply into a list, xcb returns a list of ints +# in this case +window_list = list(struct.unpack( + 'I' * clientlist_reply.value_len, clientlist_reply.value.buf())) + + +for window in window_list: + if window is 0: + continue + + # get some of the useful properties + # you can use xprop in a terminal to see whats available + window_name = xclient.GetProperty( + delete=False, + window=window, + property=xproto.Atom.WM_NAME, + type=xproto.GetPropertyType.Any, + long_offset=0, + long_length=unsigned_int_32_max_size).reply() + + window_class = xclient.GetProperty( + delete=False, + window=window, + property=xproto.Atom.WM_CLASS, + type=xproto.GetPropertyType.Any, + long_offset=0, + long_length=unsigned_int_32_max_size).reply() + + window_type = xclient.GetProperty( + delete=False, + window=window, + property=_NET_WM_WINDOW_TYPE.atom, + type=xproto.GetPropertyType.Any, + long_offset=0, + long_length=1).reply() + + window_net_name = xclient.GetProperty( + delete=False, + window=window, + property=_NET_WM_NAME.atom, + type=xproto.Atom.Any, + long_offset=0, + long_length=unsigned_int_32_max_size).reply() + + # This does the focus, set the window to have input focus + # then raise the window with configure window + xclient.SetInputFocus( + xcb.xproto.InputFocus.Parent, + window, + xcb.xproto.Time.CurrentTime) + + xclient.ConfigureWindow( + window, + xproto.ConfigWindow.StackMode, + [xproto.StackMode.Above]) + + xserver.flush() + time.sleep(0.25) + + window_class = ''.join([chr(c) for c in window_class.value]) + window_name = ''.join([chr(c) for c in window_name.value]) + window_net_name = ''.join([chr(c) for c in window_net_name.value]) + + print('-------') + print('window id = %s' % window) + print('window name = %s' % window_name) + print('window net name = %s' % window_net_name) + print('window class = %s' % window_class) +#+END_SRC + diff --git a/bases/do-blog/resources/documents/recipes/cakes/apple-cake.org b/bases/do-blog/resources/documents/recipes/cakes/apple-cake.org new file mode 100644 index 0000000..5daca35 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/cakes/apple-cake.org @@ -0,0 +1,34 @@ +#+TITLE: Apple cake recipe +#+DATE: 2018-05-23 12:00:00 UTC +#+DESCRIPTION: Simple apple cake reciepe +#+FILETAGS: recipe:cakes +#+CATEGORY: recipes +#+SLUG: apple-cake +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: ./apple-cake.jpg + +* Ingredients +- Around 3 apple's or 450g cooking apples +- juice of ½ lemon +- 225g butter, softened +- 280g golden caster sugar +- 4 egg +- 2 tsp vanilla extract +- 350g self-raising flour +- 2 tsp baking powder +- demerara sugar, to sprinkle + +* Instructions +1. Heat oven to 180C/fan 160C/gas 4. +Butter and line a rectangular baking tin (approx 27cm x 20cm) with parchment paper. +Peel, core and thinly slice the apples then squeeze the lemon juice over. +Set to one side. + +2. Mix the butter, caster sugar, eggs, vanilla, flour and baking powder until smooth. +Spread half the mixture into the prepared tin. +Arrange half the apples over the top of the mixture, then repeat the layers. +Sprinkle over the demerara sugar. + +3. Bake for 45-50 mins until golden and springy to the touch. +Leave to cool for 10 mins, then turn out of tin and remove paper. Cut into bars or squares. diff --git a/bases/do-blog/resources/documents/recipes/cakes/banana-cake.org b/bases/do-blog/resources/documents/recipes/cakes/banana-cake.org new file mode 100644 index 0000000..1d19203 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/cakes/banana-cake.org @@ -0,0 +1,45 @@ +#+TITLE: Banana cake recipe +#+DATE: 2018-05-23 12:00:00 UTC +#+FILETAGS: recipe:cakes +#+CATEGORY: recipes +#+SLUG: banana-cake +#+HTML_DOCTYPE: +#+THUMBNAIL: ./banana-cake.jpg + + + +** Ingredients + - 125g butter + - 150g caster sugar + - 1 teaspoon vanilla extract + - 1 egg, beaten + - 2 very ripe bananas, mashed + - 190g self raising flour + - 60ml milk + +** Instructions +1. Grease and line a 2lb loaf tin. Melt butter, sugar and vanilla in a saucepan over a medium heat. +2. Remove from heat and add the mashed bananas, mix well. +3. Add the egg, mix well. +4. Stir in the flour and the milk. +5. Pour into the prepared tin, sprinkle with a tablespoon of demerara sugar to give a crunch topping if liked. +6. Bake at 170 C / Fan 150 C / Gas 3 for 35 minutes, or until a skewer comes out clean. Leave to cool and enjoy! + +* Healthier ingredient version + +** Ingredients + - 125g wholemeal flour + - 2.5 tsp baking powder + - 2 tsp ground cinnamon + - 75g sultana's + - 50g melted butter + - 2 tsp vanilla essence + - 1 egg + - 1 tablespoon milk + - 3 ripe banana's + +** Instructions +1. Mix together the dry ingredients in a bowl +2. Mix the wet ingredients together +3. Combine the wet and dry ingredients +4. put in loaf tin and cook for 30 minutes diff --git a/bases/do-blog/resources/documents/recipes/cakes/chocolate-brownie.org b/bases/do-blog/resources/documents/recipes/cakes/chocolate-brownie.org new file mode 100644 index 0000000..6e275a2 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/cakes/chocolate-brownie.org @@ -0,0 +1,29 @@ +#+TITLE: Chocolate Brownies +#+DATE: 2018-05-23 12:00:00 UTC +#+FILETAGS: recipe:cakes +#+CATEGORY: recipes +#+SLUG: chocolate-brownie +#+HTML_DOCTYPE: +#+THUMBNAIL: ./chocolate-brownie.jpg + +* Ingredients +- 185g Chocolate chopped +- 185g unsalted butter +- 3 Eggs +- 275g Golden caster sugar +- 85g Plain Flour +- 40g cocoa powder +- 50g white chocolate +- 50g milk chocolate +* Instructions +1. Preheat oven to 180C Butter square cake tin + +2. Melt the chocolate with the butter and cool. +Whisk the eggs and sugar until thick and creamy, and double it original size. +Pour over the cooled chocolate and fold together. +Sift over the flour and cocoa and fold together. +Stir in the chopped chocolate + +3. Pour into the tin and bake for 25 minutes. +if it wobbles in the middle cook for longer. + diff --git a/bases/do-blog/resources/documents/recipes/cakes/crumble.org b/bases/do-blog/resources/documents/recipes/cakes/crumble.org new file mode 100644 index 0000000..7e244b8 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/cakes/crumble.org @@ -0,0 +1,35 @@ +#+TITLE: Apple & Rhubarb Crumble +#+DATE: 2018-05-23 12:00:00 UTC +#+FILETAGS: recipe:cakes +#+CATEGORY: recipes +#+SLUG: apple-rhubarb-crumble +#+HTML_DOCTYPE: +#+THUMBNAIL: ./crumble-pie.jpg + + +* Ingredients Crumble +- 140g self-raising flour +- 85g butter +- Butter, chilled +- 50g light brown muscovado sugar + +* Ingredients Apple & Rhubarb +- 3 apples +- 350 grams rhubarb +- 2 tables spoons of sugar +- 3 table spoons of orange juice + +* Instructions Crumble +1. Heat oven to 200C/180C fan/gas 6. +Rub the flour and butter together with your fingers until you have a soft. crumbly topping. +Now add the sugar and nuts, mixing together with your hands. +Scatter the topping over the rhubarb and bake for 30 mins or until golden brown on top. +Serve piping hot with a big jug of Thick vanilla custard (see below). + +* Instructions Apple & Rhubarb +Chop the apple into small slices +Chop the rhubarb into small chunks +Put in a dish sprinkle the sugar add the orange +Cook for 30 minutes + + diff --git a/bases/do-blog/resources/documents/recipes/cookies/gingerbread-people.org b/bases/do-blog/resources/documents/recipes/cookies/gingerbread-people.org new file mode 100644 index 0000000..d06d0f6 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/cookies/gingerbread-people.org @@ -0,0 +1,30 @@ +#+TITLE: Gingerbread People +#+DATE: 2023-06-20 12:00:00 UTC +#+FILETAGS: recipe:cookie +#+CATEGORY: recipes +#+SLUG: gingerbread-people +#+HTML_DOCTYPE: +#+THUMBNAIL: ./gingerbread-people.jpg + +* Ingredients + - 3 cups flour, plain/all purpose + - 3 tsp baking powder + - 1 tbsp ground ginger + - 2 tsp ground cinnamon + - 1 tsp mixed spice + - 1/4 tsp ground cloves (optional) + - 85g / 5 1/2 tbsp unsalted butter , softened (Note 3) + - 3/4 cup brown sugar (Note 4 re: light or dark brown) + - 1 large egg , at room temperature (Note 5) + - 2 tsp vanilla extract + - 1/2 cup molasses, light / true (NOT blackstrap)(Note 6) + +* Instructions + +1. Mix the dry Ingredients flour, baking powder, ginger, cinnamon, mixed spice, cloves +2. Cream the butter and sugar in a separate bowl, mix with mixer or by hand +3. add egg & vanilla and mix followed my molasses, treacle or golden syrup. +4. Mix in the dry ingredients bit by mix and mix +5. Refrigerate for around an hour +6. Roll out and cut your shapes +7. cook in the oven for around 10-12 minutes depending on offer and how crisp you would like them. diff --git a/bases/do-blog/resources/documents/recipes/cookies/karens-cookie.org b/bases/do-blog/resources/documents/recipes/cookies/karens-cookie.org new file mode 100644 index 0000000..fec7a92 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/cookies/karens-cookie.org @@ -0,0 +1,28 @@ +#+TITLE: Karen's cookie +#+DATE: 2018-05-23 12:00:00 UTC +#+FILETAGS: recipe:cookie +#+CATEGORY: recipes draft +#+SLUG: karens-cookie +#+HTML_DOCTYPE: +#+THUMBNAIL: ./karen-cookie.jpg + +* Ingredients + - 175g raisins (or other fruits like apricot) + - 1 cup green tea or earl grey + - 140g plain flour + - 2 tsp ground cinnamon + - 1 tsp salt + - 140g soft brown sugar + - 65g demerara sugar + - 155g butter + - 1 large egg + - 155g rolled oats + - 1 tsp vanilla extract + +* Instructions + +1. Soak the raisins in the tea for 30 minutes (optional) +2. Mix the flour, cinnamon, salt, butter and sugar for 5 minutes or until fluffy and pale. +3. Mix in the egg, vanilla, oats and raisins by hand +4. Form the mixture into a long sausage wrapped in cling film and refrigerate until firm +5. preheat the oven to 170c cut the sausage into discs and bake for 15 minutes diff --git a/bases/do-blog/resources/documents/recipes/drinks/cordials.org b/bases/do-blog/resources/documents/recipes/drinks/cordials.org new file mode 100644 index 0000000..e7b9ede --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/drinks/cordials.org @@ -0,0 +1,21 @@ +#+TITLE: Cordials +#+DATE: 2020-06-27 12:00:00 UTC +#+DESCRIPTION: Cordials +#+FILETAGS: recipe:drinks +#+CATEGORY: recipe +#+SLUG: cordials +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: ./cordials.jpg + +* Rhubarb cordial +** Ingredients +- 450g Rhubarb +- 300g sugar (golden caster) +- 1 lemon juiced or from carton +- 1 orange juiced or from carton +- 1 slice of ginger + +** Instructions +Cook in pan on the hob or in pressure cooker until Rhubarb has turned to mush and sugar has dissolved. +Sieve out the juice and place in sterilised containers. diff --git a/bases/do-blog/resources/documents/recipes/fermenting/bread-maker-raisin-bread.org b/bases/do-blog/resources/documents/recipes/fermenting/bread-maker-raisin-bread.org new file mode 100644 index 0000000..cbd3dc8 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/bread-maker-raisin-bread.org @@ -0,0 +1,25 @@ +#+TITLE: Bread Machine Raisin Bread +#+DATE: 2022-10-13 12:00:00 UTC +#+DESCRIPTION: Cinemon & raisin bread baked in a bread machine +#+FILETAGS: recipe:bread:fermenting +#+CATEGORY: recipes +#+SLUG: bread-machine-raisin-bread +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: test + +* Ingredients +- 1 cup water +- 2 tablespoons margarine or butter +- 3 cups flour +- 3 tablespoons sugar +- 1 1⁄2 teaspoons salt +- 1 teaspoon cinnamon +- 2 1⁄2 teaspoons yeast +- 3⁄4 cup raisins + + +* Instructions +Add all the ingredients in order given above except the raisins. +Add raisins when partially needed or on machine add in beep. +Bake for around 3:30 or on your sweet bread setting diff --git a/bases/do-blog/resources/documents/recipes/fermenting/cider.org b/bases/do-blog/resources/documents/recipes/fermenting/cider.org new file mode 100644 index 0000000..fe3f294 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/cider.org @@ -0,0 +1,32 @@ +#+TITLE: Apple Cider +#+DATE: 2018-05-23 12:00:00 UTC +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+DESCRIPTION: Apple cider recipe +#+FILETAGS: recipe:fermenting:draft +#+CATEGORY: recipe +#+SLUG: ginger-wine +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt + +* Ingredients +- As many apples as you can get +- Camden tablets +Optional +- yeast +- sugar + +* Equipment +Demi john or Fermenting bin + +* Instructions +1. Press or blend as much juice out of the apples you can. +2. Place juice in demi john or Fermenting bin with an air lock, if you add yeast it should bubble quite quickly if you go more natural route it can take a week or two before you see the bubbles start. +3. Leave until the bubbles stop passing through the air lock +4. Decant into another container or bottles leaving the sediment behind. + + +* Tips +If using shop bought apples add yeast, these will have been waxed and treated which will have killed of the natural yeast. +Adding yeast will speed up the process but is not essential the apples will make there own yeast you can however get a bad batch. + diff --git a/bases/do-blog/resources/documents/recipes/fermenting/elderflower-syrup.org b/bases/do-blog/resources/documents/recipes/fermenting/elderflower-syrup.org new file mode 100644 index 0000000..bdd1ea2 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/elderflower-syrup.org @@ -0,0 +1,23 @@ +#+TITLE: Elderflower syrup +#+DATE: 2020-06-27 12:00:00 UTC +#+DESCRIPTION: Elderflower syrup recipe +#+FILETAGS: recipe:fermenting +#+CATEGORY: recipe +#+SLUG: elderflower-syrup +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: ./elderflower-syrup.jpg + +[[./elderflower-syrup.jpg]] + +* Ingredients +- 20 elderflower head's separate petals from the green you only need the petals +- 1kg granulated sugar +- 3pts water +- 2g citric acid or 120ml (2/3 chopped lemons) + +* Instructions +1. Dissolve the sugar in the water boiling in a pan +2. put the result in a bowl allowing to cool add the petals and lemon +4. leave for 4 days but stir daily +5. strain and store after the 4 days. diff --git a/bases/do-blog/resources/documents/recipes/fermenting/ginger-wine.org b/bases/do-blog/resources/documents/recipes/fermenting/ginger-wine.org new file mode 100644 index 0000000..5d4372f --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/ginger-wine.org @@ -0,0 +1,31 @@ +#+TITLE: Ginger Wine +#+DATE: 2018-05-23 12:00:00 UTC +#+DESCRIPTION: Ginger wine reciepe +#+FILETAGS: recipe:fermenting:draft +#+CATEGORY: recipe +#+SLUG: ginger-wine +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt + +* Ingredients +- 350g Root ginger +- 500g Sultanas +- 3 Kilos white sugar +- 1Kg Soft Brown Sugar +- 2 Lemon's (peeled) +- 3 Tea bags (for the tannin) + +* Instructions +1. Peel & bruise ginger root & simmer in 4 pints of water for 30 minutes. +2. Wash sultanas and simmer in 2 pints of water for 20 minutes. +3. Pour ginger & water into a fermenting bin Add the water from the Sultanas. +4. Blend the Sultanas in a food processor and add to the fermenting bin. +5. Add the lemons and tea bags and wait till its cool then add the wine yeast. +6. Ferment for 5 - 7 days, then strain the solids through muslin & squeeze out any juice leave to cool. +7. Dissolve white sugar in 6 pints of water, stir into the ginger mixture. +8. Top up to 3 gallons Ferment to almost dry. +9. Dissolve brown sugar into 4 pints of water add to fermentation bin and top up to 4 gallons. +10. This can now be transferred to demi john's for final fermentation. + +* Quantity +These instruction make around 20 Litres of wine. diff --git a/bases/do-blog/resources/documents/recipes/fermenting/kombucha.org b/bases/do-blog/resources/documents/recipes/fermenting/kombucha.org new file mode 100644 index 0000000..a7ebcde --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/kombucha.org @@ -0,0 +1,22 @@ +#+TITLE: Kombucha +#+DATE: 2024-10-21 12:00:00 UTC +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+DESCRIPTION: Kombucha +#+FILETAGS: recipe:fermenting:draft +#+CATEGORY: recipe +#+SLUG: kombucha +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt + +For 1 litre: Add 10g of tea (2 teabags) to the water. + +For 2 litre: Add 20g of tea (4 teabags) to the water. + +For 5 litre: Add 50g of tea (10 teabags) to the water. + +For 1 litre Kombucha Scoby: Add 900ml of non-chlorinated water to your saucepan and add 60g of sugar. + +For 2 litre Kombucha Scoby: Add 1800ml of non-chlorinated water to your saucepan and add 120g of sugar. + +For 5 litre Kombucha Scoby: Add 4500ml of non-chlorinated water to your saucepan and add 300g of sugar. diff --git a/bases/do-blog/resources/documents/recipes/fermenting/sloe-gin.jpg b/bases/do-blog/resources/documents/recipes/fermenting/sloe-gin.jpg new file mode 100644 index 0000000..fbbd9fb Binary files /dev/null and b/bases/do-blog/resources/documents/recipes/fermenting/sloe-gin.jpg differ diff --git a/bases/do-blog/resources/documents/recipes/fermenting/sloe-gin.org b/bases/do-blog/resources/documents/recipes/fermenting/sloe-gin.org new file mode 100644 index 0000000..f01fa63 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/sloe-gin.org @@ -0,0 +1,23 @@ +#+TITLE: Sloe gin +#+DATE: 2018-05-23 12:00:00 UTC +#+DESCRIPTION: Sloe Gin recipe +#+FILETAGS: recipe:fermenting +#+CATEGORY: recipe +#+SLUG: sloe-gin +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: ./sloe-gin.jpg + +[[./sloe-gin.jpg]] + +* Ingredients +- 500g Sloe's or damson's foraged from a hedgerow +- 250g caster sugar +- 1 Litre of gin + +* Instructions +1. Prick the sloe's with a pin, alternatively freeze and the skins will hopefully crack. +2. place sloes and sugar in a bottle and pour in the gin +3. Shake for about a week once a day to help the sugar dissolve. +4. Leave for about 3/6 month's then strain the sloes and rebottle you dont want to see bad sloe's. +5. Drink or leave for about a year or more for an improved flavour. diff --git a/bases/do-blog/resources/documents/recipes/fermenting/sour-dough-bread.org b/bases/do-blog/resources/documents/recipes/fermenting/sour-dough-bread.org new file mode 100644 index 0000000..ffff8a8 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/sour-dough-bread.org @@ -0,0 +1,36 @@ +#+TITLE: Sourdough Bread +#+DATE: 2019-02-19 12:00:00 UTC +#+DESCRIPTION: Simple sour dough bread recipe +#+FILETAGS: recipe:bread:fermenting +#+CATEGORY: recipes +#+SLUG: simple-sourdough-bread +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: ./sour-dough-bread.jpg + +* Ingredients +- Starter Sourdough +- Bread flour +- Warm water + +* Instructions +Put around a 3rd of your starter in a bowl add flour and warm water to the quantity required. +Mix together into a dough this should not be runny in any way but should not be super dry hopefully it will be sticky to touch and be less sticky after left to sit, you will work this out over time. + +Place the dough into a loaf tin leave in a warm place next to a radiator works well, after around 6 hours it should have doubled in size this varies greatly depending on the amount of heat. + +Place the expanded dough into a preheated oven at 220 degrees for 20 - 25 minutes the bread should rise even more, then switch of the oven and leave in the oven to cool, use a skewer to check if unsure it should come out clean. + + +* Bread Tips +1. Mix the ingredients and leave for 15 - 30 minutes or longer, this will make the bread nicer to deal with. + +2. Stretch the dough to find out how stretch it is the more gluten forms the more stretch. + +3. Most recipes tell you to kneed the dough which may give a lighter loaf but I have found it still works well with out this if you time it well. + +* Starter Tips + +1. A layer of liquid on top of your starter is fine, this is just gluten mix it back in and carry on. + +2. An active starter will expand in the warm and have air bubbles formed on the surface diff --git a/bases/do-blog/resources/documents/recipes/fermenting/sour-dough-tortillas.org b/bases/do-blog/resources/documents/recipes/fermenting/sour-dough-tortillas.org new file mode 100644 index 0000000..c0cd752 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/sour-dough-tortillas.org @@ -0,0 +1,25 @@ +#+TITLE: Sourdough Tortillas +#+DATE: 2020-05-17 12:00:00 UTC +#+DESCRIPTION: Simple tortilla recipe +#+FILETAGS: recipe:bread:fermenting +#+CATEGORY: recipes +#+SLUG: simple-tortilla-bread +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: ./sourdough-tortilla-bread.jpg + +* Ingredients +- Starter Sourdough (active or inactive) 75grams +- 150g warm water +- 25g vegetable oil +- 1 teaspoon salt +- 300g flour +- half a teaspoon baking powder + +* Instructions + +Mix the Starter, water, oil, salt, flour and baking powder in a bowl and mix into a dough ball, if the dough is sticky add flour if its very dry add water your want some where in the middle. + +Leave for around 30 minutes then split into small golf ball sized spheres, then roll out as thin as you can or to the size of your frying pan. + +Make sure the pan is hot and cook for about a minute on either side. diff --git a/bases/do-blog/resources/documents/recipes/fermenting/vinegar.org b/bases/do-blog/resources/documents/recipes/fermenting/vinegar.org new file mode 100644 index 0000000..6676a21 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/fermenting/vinegar.org @@ -0,0 +1,30 @@ +#+TITLE: Vinegar +#+DATE: 2023-02-04 12:00:00 UTC +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+DESCRIPTION: Vinegar making basics +#+FILETAGS: recipe:fermenting +#+CATEGORY: recipe +#+SLUG: vinegar +#+HTML_DOCTYPE: +#+SETUPFILE: ./templates/org/readthedocs.txt +#+THUMBNAIL: ./vinegar.jpg + +* Ingredients +- Water filtered, no chlorine as this can kill the bacteria +- Sugar around 3 tablespoons per pint +- Fruit like apples + +* Instructions +1. Place fruit into a jar, you can use apple cores or whole apples chopping will increase the surface area and help infuse your vinegar. +2. Fill with clean filtered water +3. Add in some sugar the amount will vary for quantity your making and fruits sugar content. +4. Leave it for around 4 weeks in a dark place, then remove the fruit and leave for another 4 weeks, at which point it should be ready to use. + +* Tips +Make sure your jar can release the gas which builds up during fermentation, cloth with elastic or fermenting jars or bottles. + +Boiling water for 15 to 20 minutes can remove the chlorine, leaving it in an open container over night can also work as the chlorine will evaporate over time, the reason wee need clean water is the chemicals in the water can kill the bacteria required to make the vinegar. + +You can speed up the process by adding some active vinegar, this is vinegar with the bacteria till active shop bought vinegar will often have gone through a process which kills the bacteria. + diff --git a/bases/do-blog/resources/documents/recipes/general/banana-pancakes.org b/bases/do-blog/resources/documents/recipes/general/banana-pancakes.org new file mode 100644 index 0000000..e15d971 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/general/banana-pancakes.org @@ -0,0 +1,24 @@ +#+TITLE: Banana pancakes +#+DATE: 2021-12-20 12:00:00 UTC +#+FILETAGS: recipe:pancackes +#+CATEGORY: recipes +#+SLUG: banana-panckes +#+HTML_DOCTYPE: +#+THUMBNAIL: ./banana-pancakes.jpg + +* Ingredients + - 1-1/2 cups all purpose flour, spooned into measuring cup and leveled off + - 2 tablespoons sugar + - 2-1/2 teaspoons baking powder + - 1/2 teaspoon salt + - 1 small, over-ripe banana, peeled (the browner, the better) + - 2 large eggs + - 1 cup plus 2 tablespoons low fat milk + - 1/2 teaspoon vanilla extract + - 3 tablespoons melted butter + +* Instructions +1. Mix together dry ingredients Flour, Sugar, Salt, Baking powder +2. Mash the banana then mix in the Eggs, Milk & vanilla +3. Combine all the ingredients adding the melted butter +4. Add oil and butter to the pan and spoon into the pan cooking at a low heat so they cook through and look golden brown when cooked. diff --git a/bases/do-blog/resources/documents/recipes/general/shakshuka.org b/bases/do-blog/resources/documents/recipes/general/shakshuka.org new file mode 100644 index 0000000..dc45f39 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/general/shakshuka.org @@ -0,0 +1,25 @@ +#+TITLE: Shakshuka +#+DATE: 2021-12-20 12:00:00 UTC +#+FILETAGS: recipe +#+CATEGORY: recipes +#+SLUG: shakshuka +#+HTML_DOCTYPE: +#+THUMBNAIL: ./shakshuka.jpg + +* Ingredients + - 3 tablespoons olive oil + - 1 onion + - 1 bell pepper + - 3 cloves garlic + - 1.5 tablespoons paprika + - 2 teaspoons cumin seeds + - 2 tins 800g tomatoes + - pinch of salt + - handful of parsley + - 6 large eggs + +* Instructions + +1. Add the oil and fry the onion and pepper add salt softening & charing, add spices and garlic +2. Add tomatoes and parsley simmer +3. make wells and add eggs in the well and allow to cook diff --git a/bases/do-blog/resources/documents/recipes/general/sweet-chestnut-mushroom-pie.org b/bases/do-blog/resources/documents/recipes/general/sweet-chestnut-mushroom-pie.org new file mode 100644 index 0000000..d90b104 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/general/sweet-chestnut-mushroom-pie.org @@ -0,0 +1,38 @@ +#+TITLE: Sweet chestnut & mushroom pie +#+DATE: 2021-03-16 12:00:00 UTC +#+FILETAGS: recipe:general:pie +#+CATEGORY: recipes draft +#+SLUG: sweet-chestnut-mushroom-pie +#+HTML_DOCTYPE: +#+THUMBNAIL: ./default.jpg + +* Ingredients + 50g/1¾oz butter + 2 leeks, sliced + 350g/12oz mushrooms (such as chestnut or button mushrooms) + 2 garlic cloves, sliced + large sprig thyme, leaves only + 15g/½oz dried mushrooms (optional), soaked in 150ml/5fl oz freshly boiled water + 50g/1¾oz plain flour + 1 tsp Dijon mustard + 100ml/3½fl oz marsala + 200–300ml/7–10fl oz vegetable or chicken stock (amount depending on whether or not you are using dried mushrooms) + 200g roasted chestnuts whole + + +* Instructions +To make the filling, put the butter in a large saucepan and melt over a low heat. Add the leeks and cover. Fry for 5 minutes. + +Turn up the heat slightly and add the mushrooms. Cook for a further 4–5 minutes, then add the garlic and thyme. If using, drain the rehydrated mushrooms, reserving their soaking liquor, and roughly chop. Add to the saucepan. + +Add the flour and stir until you can see a roux has formed around the vegetables. + +Stir in the mustard, then add the marsala and stir until it is well incorporated. Gradually add the stock and the mushroom liquor, if using. (If not using the mushroom liquor, use the larger quantity of stock.) Bring the mixture to the boil, then turn down and simmer, continuing to stir, until it has thickened. Add the chestnuts, cover, and remove from the heat. Leave to cool. + +Preheat the oven to 190C/170C Fan/Gas 5. + +Divide the pastry into two pieces, one slightly bigger than the other. Roll out the larger piece of pastry and use it to line a pie dish. Add the cooled filling. + +Roll out the remaining piece of pastry and top the pie, wetting the edges with the egg wash, making sure they are well crimped together. Cut a couple of slits in the pie to let steam escape and brush with egg wash. + +Bake in the oven for around 40–45 minutes, or until the crust is golden brown. Serve in slices. diff --git a/bases/do-blog/resources/documents/recipes/general/tattie-scones.org b/bases/do-blog/resources/documents/recipes/general/tattie-scones.org new file mode 100644 index 0000000..dd94165 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/general/tattie-scones.org @@ -0,0 +1,22 @@ +#+TITLE: Tattie (potato) scones +#+DATE: 2021-03-16 12:00:00 UTC +#+FILETAGS: recipe:general:potato +#+CATEGORY: recipes draft +#+SLUG: potato-scones +#+HTML_DOCTYPE: +#+THUMBNAIL: ./potato-scones.jpg + +* Ingredients +- 500g potatoes (Around 4 baking potatoes, mashed) +- 25g butter melted +- 1 teaspoon salt +- 2 eggs +- 2 teaspoons baking powder +- 250g flour + +Cook and add anything else you want like cooked onions or herbs to add flavor add more flour if needed to help dry out the mix. + +* Instructions +Make the mash potato make a dry mash if there is to much water in your mash it will not roll out as nicely, add and mix in all the other Ingredients then roll out and cut to any shape you like. + +Cook in an oven for 15 minutes or in a frying pan waiting until they are browned and have risen then serve. diff --git a/bases/do-blog/resources/documents/recipes/jams-chutneys/greengage-chutney.org b/bases/do-blog/resources/documents/recipes/jams-chutneys/greengage-chutney.org new file mode 100644 index 0000000..993855b --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/jams-chutneys/greengage-chutney.org @@ -0,0 +1,27 @@ +#+TITLE: Greengage Chutney +#+DATE: 2021-10-02 12:00:00 UTC +#+FILETAGS: recipe:chutney +#+CATEGORY: recipes +#+SLUG: greengage-chutney +#+HTML_DOCTYPE: +#+THUMBNAIL: ./default.jpg + +* Ingredients + - 1kg greengages, washed and stoned and cut into segments + - 2 small brown onions, peeled and finely chopped + - 2 large apples peeled and diced + - 200g sultanas + - 1 tbsp grated fresh root ginger + - 1tsp cinnamon + - ½ tsp turmeric + - 2 tsp salt + - 500/600ml white wine or cider vinegar + - 400g granulated sugar + +* Instructions + 1 Place all the ingredients except the sugar, into a heavy based saucepan and stir well. + 2 Bring to the boil whilst stirring and then simmer for around 10 minutes until the fruit is soft. + 3 Add the sugar and keep stirring on the heat until it has completely dissolved. + 4 Bring to the boil again and keep cooking on high, stirring so that it doesn’t catch and until most of the liquid has evaporated and the mix feels thicker and looks glossy. + 5 Remove from the heat and whilst still warm place into sterilised jars. + 6 Best left for 2-3 weeks to allow the spices and flavours to really develop. diff --git a/bases/do-blog/resources/documents/recipes/puddings/bread-pudding.org b/bases/do-blog/resources/documents/recipes/puddings/bread-pudding.org new file mode 100644 index 0000000..08eb7a9 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/puddings/bread-pudding.org @@ -0,0 +1,33 @@ +#+TITLE: Bread pudding recipe +#+DATE: 2018-05-23 12:00:00 UTC +#+FILETAGS: recipe:puddings +#+CATEGORY: puddings +#+SLUG: bread-pudding +#+HTML_DOCTYPE: +#+THUMBNAIL: ./bread-pudding.jpg + +* Ingredients + - 500g white or wholemeal bread + - 500g/1lb 2oz mixed dried fruit + - 1 ½ tbsp mixed spice + - 600ml milk + - 2 large eggs, beaten + - 140g light muscovado sugar + - 100g Butter, melted + - 2 tbsp demerara sugar +*** optional + - zest 1 lemon Lemon (optional) + - 85g mixed peel + - sultanas instead of dried fruit + +* Instructions +1. Tear the bread into a large mixing bowl and add the fruit, peel and spice. +Pour in the milk, then stir or scrunch through your fingers to mix everything well and completely break up the bread. +Add eggs, muscovado and lemon zest if using. Stir well, then set aside for 15 mins to soak. + +2. Heat oven to 180C/160C fan/gas 4. +Butter and line the base of a 20cm non-stick square cake tin (not one with a loose base). +Stir the melted butter into the pudding mixture, tip into the tin, then scatter with demerara. +Bake for 1½ hrs until firm and golden, covering with foil if it starts to brown too much. +Turn out of the tin and strip off the paper. +Cut into squares and serve warm. diff --git a/bases/do-blog/resources/documents/recipes/puddings/rice-pudding.org b/bases/do-blog/resources/documents/recipes/puddings/rice-pudding.org new file mode 100644 index 0000000..531dc05 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/puddings/rice-pudding.org @@ -0,0 +1,23 @@ +#+TITLE: Rice pudding +#+DATE: 2018-05-23 12:00:00 UTC +#+FILETAGS: recipe:puddings +#+CATEGORY: puddings +#+SLUG: rice-pudding +#+HTML_DOCTYPE: +#+THUMBNAIL: ./rice-pudding.jpg + +* Info +Serves 4 + +* Ingredients + - 110g (4 oz) pudding rice + - 25g (1 oz) granulated sugar + - 25g (1 oz) margarine or butter + - 12L (2 pt) milk + - ground cinnamon, nutmeg or vanilla extract (optional) +* Instructions +1. Use some of the butter to grease the bowl of the slow cooker. +2. Add milk, rice, sugar and remaining butter and either ground cinnamon, nutmeg or vanilla extract. +3. Give the contents a stir and replace the lid. +4. Turn the slow cooker onto high and cook for 3-4 hours or until the rice as absorbed the liquid. +5. Stir around halfway through. diff --git a/bases/do-blog/resources/documents/recipes/puddings/spotted-dick.org b/bases/do-blog/resources/documents/recipes/puddings/spotted-dick.org new file mode 100644 index 0000000..7bfa775 --- /dev/null +++ b/bases/do-blog/resources/documents/recipes/puddings/spotted-dick.org @@ -0,0 +1,39 @@ +#+TITLE: Spotted Dick +#+DATE: 2018-05-23 12:00:00 UTC +#+FILETAGS: recipe:puddings +#+CATEGORY: puddings +#+SLUG: spotted-dick +#+HTML_DOCTYPE: +#+THUMBNAIL: ./spotted-dick.jpg + +* Ingredients +- 100g flour +- Half a teaspoon of baking powder +- A handful of raisins +- A tablespoon of cinnamon +- 1 pinch of salt +- 100g butter +- 100g sugar +- 2 large eggs +- A dribble of milk + + +* Instructions +Put the sifted flour, baking powder and salt into a bowl. In another bowl, put the sugar and butter. +rub the butter and sugar together really well until they become consistent. +Add one egg and a tablespoonful of flour. +Fold it into the mixture. +Repeat with the second egg. +Now put in the remaining flour, the raisins and a dribble of milk to help the moistness. Finally, add the cinnamon. + + +Your preparation should look something like this: + + +Put the mixture into a heatproof bowl of about 750ml +seal it with tin foil and put it into boiling water, +making sure no water is high enough to not enter the bowl +(I put a jam pot lid in the bottom of the saucepan to keep it up). + +You will need to boil it for about 2 hours. + diff --git a/bases/do-blog/resources/documents/reviews/appaccomplished.org b/bases/do-blog/resources/documents/reviews/appaccomplished.org new file mode 100755 index 0000000..c7fed32 --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/appaccomplished.org @@ -0,0 +1,37 @@ +#+TITLE: Review of "App Accomplished" +#+DATE: 2015-03-08 12:00:00 UTC +#+DESCRIPTION: Short review of "App Accomplished" +#+FILETAGS: reviews:python +#+CATEGORY: python +#+SLUG: review-of-app-accomplised +#+LINK: http://www.amazon.com/App-Accomplished-Strategies-Development-Success/dp/0321961781/ref=sr_1_1?s=books&ie=UTF8&qid=1420814440&sr=1-1&keywords=app+accomplished+strategies+for+app+development+success +#+THUMBNAIL: ../../images/appaccomplished.jpg +#+BEGIN_COMMENT +.. title: Review of "App Accomplished" +.. slug: review-of-app-accomplised +.. date: 2015-03-08 12:00:00 UTC +.. tags: reviews, python +.. category: python +.. link: http://www.amazon.com/App-Accomplished-Strategies-Development-Success/dp/0321961781/ref=sr_1_1?s=books&ie=UTF8&qid=1420814440&sr=1-1&keywords=app+accomplished+strategies+for+app+development+success +.. description: Short review of "App Accomplished" +.. type: text +#+END_COMMENT + +#+CAPTION: App Accomplished +[[../../images/appaccomplished.jpg]] + + +I found this book to be well written and it explains the life cycle of modern application development very well. +If you are a developer who has worked in any decent software houses you will likely find the information +in this book very familiar and not of interest. + +This book does cover, in detail, the process of working with a client and developing an application based on the client's +requirements in addition to how to go about designing, testing and reporting bugs in the process. It also includes some amusing +stories on how not to do things and failed projects the developer has walked into. + +I was anticipating a lot more information about the actual release process. As a developer who has written +many web and desktop applications I was hoping for more information about the actual selling of apps and what +I need to know about any pitfalls or things you may need to be aware of. + +If you are a new or self-taught developer, or if you have no development experience and want to develop an application, +then this book is highly recommended and should help you avoid common mistakes. diff --git a/bases/do-blog/resources/documents/reviews/appaccomplished.py b/bases/do-blog/resources/documents/reviews/appaccomplished.py new file mode 100755 index 0000000..2a30a55 --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/appaccomplished.py @@ -0,0 +1,51 @@ +#append this to module path so we can load from the parent directory +import os +import sys +from scaffold import web +from scaffold.loaders import load + + +class page: + name = __name__ + title = 'Review of "App Accomplished"' + tags = 'review', 'books' + intro = 'Short review of "App Accomplished"' + date = '2015-03-08' + image = web.template.path_image + 'thumbs/appaccomplished.jpg' + + def create_content(self): + web.link.create( + 'App Accomplished', + web.images.create( + self.image, + title='App Accomplished' + ).render(), + 'http://www.amazon.com/App-Accomplished-Strategies-Development-Success/dp/0321961781/ref=sr_1_1?s=books&ie=UTF8&qid=1420814440&sr=1-1&keywords=app+accomplished+strategies+for+app+development+success') + web.page.section( + web.div.create( + web.link.render() + ).set_classes('sources').render() + ) + + web.div.create( + web.paragraph.create( + """I found this book to be well written and it explains the life cycle of modern application development very well. + If you are a developer who has worked in any decent software houses you will likely find the information + in this book very familiar and not of interest.""").render() + ).append( + web.paragraph.create( + """This book does cover, in detail, the process of working with a client and developing an application based on the client's + requirements in addition to how to go about designing, testing and reporting bugs in the process. It also includes some amusing + stories on how not to do things and failed projects the developer has walked into.""").render() + ).append( + web.paragraph.create( + """I was anticipating a lot more information about the actual release process. As a developer who has written + many web and desktop applications I was hoping for more information about the actual selling of apps and what + I need to know about any pitfalls or things you may need to be aware of.""").render() + ).append( + web.paragraph.create( + """If you are a new or self-taught developer, or if you have no development experience and want to develop an application, + then this book is highly recommended and should help you avoid common mistakes.""").render() + ) + web.page.section(web.div.render()) + diff --git a/bases/do-blog/resources/documents/reviews/effectivepython.org b/bases/do-blog/resources/documents/reviews/effectivepython.org new file mode 100755 index 0000000..ba05107 --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/effectivepython.org @@ -0,0 +1,31 @@ +#+TITLE: Review of "Effective Python" +#+DATE: 2015-08-19 12:00:00 UTC +#+DESCRIPTION: Short review of "Effective Python" +#+FILETAGS: reviews:python +#+CATEGORY: python +#+SLUG: review-of-effective-python +#+LINK: http://www.amazon.co.uk/Effective-Python-Specific-Software-Development/dp/0134034287 +#+THUMBNAIL: ../../images/effectivepython.jpg +#+BEGIN_COMMENT +.. title: Review of "Effective Python" +.. slug: review-of-effective-python +.. date: 2015-08-19 12:00:00 UTC +.. tags: review, python +.. category: python +.. link: http://www.amazon.co.uk/Effective-Python-Specific-Software-Development/dp/0134034287 +.. description: Short review of "Effective Python" +.. type: text +#+END_COMMENT + +#+CAPTION: Effective Python +[[../../images/effectivepython.jpg]] + +Effective python is a very handy book to have around. It's setup in a way that allows you to quickly jump to the information you require and get some quick tips or just jog your memory. The information provided is relevant to both python 2 & 3, which I found quite interesting as it highlighted some of the improvements in python 3. + +I particularly found the information on generators and co-routines very informative. The examples have inspired me to try and move to python 3, but unfortunately some of my projects still require a few third party libraries that only support python 2. + +The information contained within is also relevant to web development, application development, mobile applications and scientific programming. Unlike a lot of python books it does not have a specific focus on web development and focuses more on the tools of the language and how to use them to your advantage, whatever you're working on. + +You will likely find a lot of information you already know if you have worked with python for any length of time, however there are enough tips that developers will learn something new they can apply in the software they write. + +Highly recommended. diff --git a/bases/do-blog/resources/documents/reviews/effectivepython.py b/bases/do-blog/resources/documents/reviews/effectivepython.py new file mode 100755 index 0000000..cd914db --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/effectivepython.py @@ -0,0 +1,37 @@ +#append this to module path so we can load from the parent directory +import os +import sys +from scaffold import web +from scaffold.loaders import load +from scaffold.readers.markdown_reader import markdown_reader + + +class page: + name = __name__ + title = 'Review of "Effective Python"' + tags = 'review', 'books', 'python' + intro = 'Short review of "Effective Python"' + date = '2015-08-19' + image = web.template.path_image + 'thumbs/effectivepython.jpg' + + def create_content(self): + web.link.create( + 'Effective Python', + web.images.create( + self.image, + title='Effective Python' + ).render(), + 'http://www.amazon.co.uk/Effective-Python-Specific-Software-Development/dp/0134034287') + + web.page.section( + web.div.create( + web.link.render() + ).set_classes('sources').render() + ) + + web.page.section( + markdown_reader( + os.path.abspath('./content/reviews/effectivepython.md') + ).render() + ) + diff --git a/bases/do-blog/resources/documents/reviews/genericprogramming.html b/bases/do-blog/resources/documents/reviews/genericprogramming.html new file mode 100755 index 0000000..c68c077 --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/genericprogramming.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + +
+ +
+

frommathmaticstogenericprogramming.jpeg +

+

Figure 1: From Mathematics to Generic Programming

+
+ + +

+From Mathematics to Generic Programming is part history, part mathematics and part programming with the emphasis very much on the maths side. +I found the book quite hard to follow, mainly because I don't use mathematics often enough. +Nevertheless, I found the idea that you can gain such versatility from a single algorithm quite interesting. +

+ +

+It has sparked my interest enough to re-learn some of the maths I once knew. +If your algebra and geometry knowledge is reasonable, you are likely to have no trouble with the material in this book. +It is laid out with some exercises for the reader to attempt to help with understanding. +

+ +

+I would like to have seen further examples relating to the use of higher level development, for example web development, if this were possible. +

+ +

+Currently, the emphasis seems to be on low level programming which is perhaps less common, given all the rich libraries around which implement most of these functions. +

+ +

+If your maths is reasonable and you have a good understanding of lower level programming this book will be of great interest to you. +

+
+
+

Author: oly

+

Created: 2016-11-15 Tue 17:41

+

Validate

+
+ + diff --git a/bases/do-blog/resources/documents/reviews/genericprogramming.org b/bases/do-blog/resources/documents/reviews/genericprogramming.org new file mode 100755 index 0000000..c095c99 --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/genericprogramming.org @@ -0,0 +1,37 @@ +#+TITLE: Review of "From Mathematics to Generic Programming" +#+DATE: 2014-12-29 12:00:00 UTC +#+DESCRIPTION: Short review of "From Mathematics to Generic Programming" +#+FILETAGS: reviews:maths +#+CATEGORY: python +#+SLUG: review-of-from-mathematics-to-generic-programming +#+LINK: http://www.amazon.co.uk/Effective-Python-Specific-Software-Development/dp/0134034287 +#+THUMBNAIL: ../../images/frommathmaticstogenericprogramming.jpeg +#+BEGIN_COMMENT +.. title: Review of "From Mathematics to Generic Programming" +.. slug: review-of-from-mathematics-to-generic-programming +.. date: 2014-12-29 12:00:00 UTC +.. tags: review, maths +.. category: python +.. link: http://www.amazon.co.uk/Effective-Python-Specific-Software-Development/dp/0134034287 +.. description: Short review of "From Mathematics to Generic Programming" +.. type: text +#+END_COMMENT + +#+CAPTION: From Mathematics to Generic Programming +[[../../images/frommathmaticstogenericprogramming.jpeg]] + + +From Mathematics to Generic Programming is part history, part mathematics and part programming with the emphasis very much on the maths side. +I found the book quite hard to follow, mainly because I don't use mathematics often enough. +Nevertheless, I found the idea that you can gain such versatility from a single algorithm quite interesting. + +It has sparked my interest enough to re-learn some of the maths I once knew. +If your algebra and geometry knowledge is reasonable, you are likely to have no trouble with the material in this book. +It is laid out with some exercises for the reader to attempt to help with understanding. + +I would like to have seen further examples relating to the use of higher level development, for example web development, if this were possible. + +Currently, the emphasis seems to be on low level programming which is perhaps less common, given all the rich libraries around which implement most of these functions. + +If your maths is reasonable and you have a good understanding of lower level programming this book will be of great interest to you. + diff --git a/bases/do-blog/resources/documents/reviews/genericprogramming.py b/bases/do-blog/resources/documents/reviews/genericprogramming.py new file mode 100755 index 0000000..4ac6825 --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/genericprogramming.py @@ -0,0 +1,45 @@ +#append this to module path so we can load from the parent directory +import os +import sys +from scaffold import web +from scaffold.loaders import load + + +class page: + name = __name__ + title = 'Review of "From Mathematics to Generic Programming"' + tags = 'review', 'books', 'maths' + intro = 'Short review of "From Mathematics to Generic Programming"' + date = '2014-12-29' + image = web.template.path_image + 'thumbs/frommathmaticstogenericprogramming.jpeg' + + def create_content(self): + web.link.create('From Mathematics to Generic Programming', web.images.create(self.image, title='From Mathematics to Generic Programming').render(),'http://www.amazon.com/Mathematics-Generic-Programming-Alexander-Stepanov/dp/0321942043/ref=sr_1_1?ie=UTF8&qid=1418657555&sr=8-1&keywords=from+mathematics+to+generic+programming') + web.page.section( + web.div.create( + web.link.render() + ).set_classes('sources').render() + ) + + web.div.create( + web.paragraph.create( + """From Mathematics to Generic Programming is part history, part mathematics and part programming with the emphasis very much on the maths side. + I found the book quite hard to follow, mainly because I don't use mathematics often enough. + Nevertheless, I found the idea that you can gain such versatility from a single algorithm quite interesting.""").render() + ).append( + web.paragraph.create( + """It has sparked my interest enough to re-learn some of the maths I once knew. + If your algebra and geometry knowledge is reasonable, you are likely to have no trouble with the material in this book. + It is laid out with some exercises for the reader to attempt to help with understanding.""").render() + ).append( + web.paragraph.create( + """I would like to have seen further examples relating to the use of higher level development, for example web development, if this were possible.""").render() + ).append( + web.paragraph.create( + """Currently, the emphasis seems to be on low level programming which is perhaps less common, given all the rich libraries around which implement most of these functions.""").render() + ).append( + web.paragraph.create( + """If your maths is reasonable and you have a good understanding of lower level programming this book will be of great interest to you.""").render() + ) + web.page.section(web.div.render()) + diff --git a/bases/do-blog/resources/documents/reviews/opengles3.org b/bases/do-blog/resources/documents/reviews/opengles3.org new file mode 100755 index 0000000..960a46c --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/opengles3.org @@ -0,0 +1,40 @@ +#+TITLE: Review of OpenGL ES 3.0 programming guide +#+DATE: 2014-08-22 12:00:00 UTC +#+DESCRIPTION: Short review of "OpenGL ES 3.0 programming guide" +#+FILETAGS: reviews:opengl +#+CATEGORY: opengl +#+SLUG: review-of-opengles3 +#+LINK: http://www.amazon.com/OpenGL-3-0-Programming-Guide-Edition/dp/0321933885/ref=sr_1_1?ie=UTF8&qid=1407516414&sr=8-1&keywords=0321933885 +#+THUMBNAIL: ../../images/opengles3.jpeg +#+BEGIN_COMMENT +.. title: Review of OpenGL ES 3.0 programming guide +.. slug: review-of-opengles3 +.. date: 2014-08-22 12:00:00 UTC +.. tags: review, opengl +.. category: opengl +.. link: http://www.amazon.com/OpenGL-3-0-Programming-Guide-Edition/dp/0321933885/ref=sr_1_1?ie=UTF8&qid=1407516414&sr=8-1&keywords=0321933885 +.. description: Short review of "OpenGL ES 3.0 programming guide" +.. type: text +#+END_COMMENT + +#+CAPTION: Open GL ES 3.0 +[[../../images/opengles3.jpeg]] + +I found this book to be a very good introduction to opengl es. It has a nice progression through the chapters building up to +more advanced topics near the end, for example terrain generation and advanced shader effects. + +This book seems to be aimed at users new to opengl es or users transitionaing from other versions of opengl to es 3.0. +If you're familiar with other versions of opengl you will likely use this book as more of a reference to look up the +differences in the function calls. + +I was very impressed by the fact that the example code can be compiled on multiple platforms, including dekstop and mobile. +The example code uses various helper functions which get created and explained as you progress through the book. +All code will run on linux, windows, mac, andriod and ios devices with build instruction for each platform at the end +of the book. + +My main crtisism of the book would be that it explains some of the maths, but not in enough detail to be useful for someone +who wants to understaanding the maths in depth. Therefore if you want to learn exactly how some of the math works another book will likely help however I +don't think in depth understanding is essential early on. + +Most of my knowledge regarding opengl has been gained through lots of googleing, this book certainly helped me fill in blanks and explained +things in much more depth than trawling through the internet. diff --git a/bases/do-blog/resources/documents/reviews/opengles3.py b/bases/do-blog/resources/documents/reviews/opengles3.py new file mode 100755 index 0000000..b69e91e --- /dev/null +++ b/bases/do-blog/resources/documents/reviews/opengles3.py @@ -0,0 +1,47 @@ +#append this to module path so we can load from the parent directory +import os +import sys +from scaffold import web +from scaffold.loaders import load + +class page: + name = __name__ + title = 'Review of OpenGL ES 3.0 programming guide' + tags = 'review', 'opengl', 'books' + intro = 'Short review of "OpenGL ES 3.0 programming guide"' + date = '2014-08-22' + image = web.template.path_image + 'thumbs/opengles3.jpeg' + + def create_content(self): + web.link.create('OpenGL ES 3.0', web.images.create(self.image, title='OpenGL ES 3.0').render(), 'http://www.amazon.com/OpenGL-3-0-Programming-Guide-Edition/dp/0321933885/ref=sr_1_1?ie=UTF8&qid=1407516414&sr=8-1&keywords=0321933885') + web.page.section( + web.div.create( + web.link.render() + ).set_classes('sources').render() + ) + + web.div.create( + web.paragraph.create( + """I found this book to be a very good introduction to opengl es. It has a nice progression through the chapters building up to +more advanced topics near the end, for example terrain generation and advanced shader effects.""").render() + ).append( + web.paragraph.create( + """This book seems to be aimed at users new to opengl es or users transitionaing from other versions of opengl to es 3.0. +If you're familiar with other versions of opengl you will likely use this book as more of a reference to look up the +differences in the function calls.""").render() + ).append( + web.paragraph.create(""" + I was very impressed by the fact that the example code can be compiled on multiple platforms, including dekstop and mobile. +The example code uses various helper functions which get created and explained as you progress through the book. +All code will run on linux, windows, mac, andriod and ios devices with build instruction for each platform at the end +of the book.""").render() + ).append( + web.paragraph.create("""My main crtisism of the book would be that it explains some of the maths, but not in enough detail to be useful for someone +who wants to understaanding the maths in depth. Therefore if you want to learn exactly how some of the math works another book will likely help however I +don't think in depth understanding is essential early on.""").render() + ).append( + web.paragraph.create("""Most of my knowledge regarding opengl has been gained through lots of googleing, this book certainly helped me fill in blanks and explained +things in much more depth than trawling through the internet.""").render() + ) + web.page.section(web.div.render()) + diff --git a/bases/do-blog/src/com/oly/static_sites/do_blog/core.cljs b/bases/do-blog/src/com/oly/static_sites/do_blog/core.cljs index 25ae5f5..1da17db 100644 --- a/bases/do-blog/src/com/oly/static_sites/do_blog/core.cljs +++ b/bases/do-blog/src/com/oly/static_sites/do_blog/core.cljs @@ -19,6 +19,7 @@ ;; [henryw374.js-joda-locale_en-us] [com.oly.static-sites.do-blog.pages.helpers.database :refer [state document conn schema latest-article search-articles]] + [com.oly.static-sites.ui-hiccup.interface :refer [board square]] [com.oly.static-sites.do-blog.pages.home :refer [home-page]] [com.oly.static-sites.do-blog.pages.article :refer [article-page]] [com.oly.static-sites.do-blog.pages.archive :refer [archive-page]] @@ -116,6 +117,12 @@ (defn ui-search [search] [:div + (board 8 + (into [] (mapv (fn [n] {:class "fl bg-black-80 white-80" :colour "black-80" :text (str n)}) + (range 1 (* 8 9)))) + ) + + [:input.w-100.center.input-reset.ba.b--black-20.pa2.mb2 {:type "text" :placeholder "Search" @@ -287,13 +294,17 @@ (:details @document)]]) (search-articles {}))]]))) (defn ^:dev/after-load reagent-render [hiccup] - (dom/render hiccup (js/document.getElementById "app"))) + (swap! state update :refresh inc) + (dom/render [hiccup] (js/document.getElementById "app"))) (defn render-page [] - (let [current-page (r/cursor state [:current-route])] + (let [current-page (r/cursor state [:current-route]) + update (r/cursor state [:refresh])] ;; load in the latest document (fn [] + @update + (prn "render") (when (-> @current-page :data :init) ((-> @current-page :data :init) nil) ((-> @current-page :data :init) (-> @current-page :parameters :path :path))) @@ -308,7 +319,7 @@ :about [layout [about-page @current-page]] [layout [:div "default"]])))) -(defn ^:after-load init [] +(defn init [] (rfe/start! router (fn [m] @@ -317,6 +328,7 @@ ) {:use-fragment false}) + (prn "init") (GET "/documents/org-meta-index.edn" {:handler (fn [response] (->> (reader/read-string response) @@ -328,7 +340,7 @@ (d/reset-conn! conn (d/empty-db schema)) (d/transact! conn (:index @state)) (select-initial-page (-> @state :current-route :parameters :path :path)))}) - (reagent-render [render-page])) + (reagent-render render-page)) ;;(defonce start (init)) diff --git a/bases/do-blog/src/com/oly/static_sites/do_blog/pages/helpers/database.cljs b/bases/do-blog/src/com/oly/static_sites/do_blog/pages/helpers/database.cljs index 08f638c..05ae939 100644 --- a/bases/do-blog/src/com/oly/static_sites/do_blog/pages/helpers/database.cljs +++ b/bases/do-blog/src/com/oly/static_sites/do_blog/pages/helpers/database.cljs @@ -9,6 +9,7 @@ (defonce document (r/atom {})) (defonce state (r/atom {:index [] + :refresh 1 :search {:terms "" :results []} :document {:details nil :body nil} diff --git a/components/ui-hiccup/deps.edn b/components/ui-hiccup/deps.edn new file mode 100644 index 0000000..28d4733 --- /dev/null +++ b/components/ui-hiccup/deps.edn @@ -0,0 +1,4 @@ +{:paths ["src" "resources"] + :deps {} + :aliases {:test {:extra-paths ["test"] + :extra-deps {}}}} diff --git a/components/ui-hiccup/resources/ui-hiccup/.keep b/components/ui-hiccup/resources/ui-hiccup/.keep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/components/ui-hiccup/resources/ui-hiccup/.keep @@ -0,0 +1 @@ + diff --git a/components/ui-hiccup/src/com/oly/static_sites/ui_hiccup/chess.cljc b/components/ui-hiccup/src/com/oly/static_sites/ui_hiccup/chess.cljc new file mode 100644 index 0000000..467be7b --- /dev/null +++ b/components/ui-hiccup/src/com/oly/static_sites/ui_hiccup/chess.cljc @@ -0,0 +1,25 @@ +(ns com.oly.static-sites.ui-hiccup.chess) + +(defn square [{:keys [colour text class]}] + [:div.w3.h3.tc {:class class} + [:p.dib.v-mid.ma0.pa0 text]]) + +(defn board [size items] + (loop [pos 1 + squares items + row [:div.h2] + result [:div]] + (if (seq squares) + (if (= size pos) + (recur 1 + (rest squares) + [:div.h2] + (conj result (conj row (square (first squares) )))) + (recur (inc pos) + (rest squares) + (conj row (square (first squares) )) + result) + ) + result))) + + diff --git a/components/ui-hiccup/src/com/oly/static_sites/ui_hiccup/interface.cljc b/components/ui-hiccup/src/com/oly/static_sites/ui_hiccup/interface.cljc new file mode 100644 index 0000000..8d7d2b4 --- /dev/null +++ b/components/ui-hiccup/src/com/oly/static_sites/ui_hiccup/interface.cljc @@ -0,0 +1,8 @@ +(ns com.oly.static-sites.ui-hiccup.interface + (:require [com.oly.static-sites.ui-hiccup.chess :as chess])) + +(defn square [vals] + (chess/square vals)) + +(defn board [size items] + (chess/board size items)) diff --git a/components/ui-hiccup/test/com/oly/static_sites/ui_hiccup/interface_test.clj b/components/ui-hiccup/test/com/oly/static_sites/ui_hiccup/interface_test.clj new file mode 100644 index 0000000..951b044 --- /dev/null +++ b/components/ui-hiccup/test/com/oly/static_sites/ui_hiccup/interface_test.clj @@ -0,0 +1,6 @@ +(ns com.oly.static-sites.ui-hiccup.interface-test + (:require [clojure.test :as test :refer :all] + [com.oly.static-sites.ui-hiccup.interface :as ui-hiccup])) + +(deftest dummy-test + (is (= 1 1))) diff --git a/deps.edn b/deps.edn index 3648c7c..f2642ed 100644 --- a/deps.edn +++ b/deps.edn @@ -19,7 +19,8 @@ no.cjohansen/portfolio {:mvn/version "2023.07.15"} djblue/portal {:mvn/version "0.48.0"}}} - :blog {:extra-paths ["development/src" "bases/do-blog/src"] + :blog {:extra-paths ["components/ui-hiccup/src" + "development/src" "bases/do-blog/src"] :extra-deps { org.clojure/core.async {:mvn/version "1.6.673"} org.clojure/clojurescript {:mvn/version "1.11.60"} @@ -34,7 +35,7 @@ olymk2/tachyon-reagent {:git/url "https://gitlab.com/olymk2/tachyon-reagent.git" :sha "e422af7b41266f7bc90ab78a24fb93f32be61385"} olymk2/cl-eorg {:git/url "https://gitlab.com/olymk2/cl-org.git" - :sha "528e8125afcac5d7664aac80f7fbba12dd0f5d98"} + :sha "13e57f11da75516844922fe28bfd6aa7692b7c34"} } }