Adding button groups using seesaw - clojure

I am creating a GUI using seesaw for the first time and I am stuck on how to add buttons to button groups and display them(buttons) on the same frame. This is what I have so far.
(def b (button :text "Start a new Project"))
(def c (button :text "Continue an Existing Project"))
(def groups (button-group))
(flow-panel :items [(b :group groups)
(c :group groups)])
(display groups)

(button) returns a button (a component) which is not a function. If you later use it as (b :group groups), it actually tries to invoke b as if it was a function, passing it two arguments: :group and groups. That's why it fails, because it can't cast button to function.
Secondly, I believe (button) creates a regular JButton, for which the group makes little sense. Did you mean radio buttons, like (radio)?
One of these two should probably do what you expect.
Radio buttons:
(def groups (button-group))
(def b (radio :text "Start a new Project" :group groups))
(def c (radio :text "Continue an Existing Project" :group groups))
(def panel
(flow-panel :items [b c]))
(invoke-later
(-> (frame :content panel :on-close :dispose) pack! show!))
Regular buttons:
(def b (button :text "Start a new Project"))
(def c (button :text "Continue an Existing Project"))
(def panel
(flow-panel :items [b c]))
(invoke-later
(-> (frame :content panel :on-close :dispose) pack! show!))
You probably can use your (display) function instead of this (invoke-later) snippet here, but this works end-to-end for me.

Related

Cljfx: two equal map's keys

I want to get this behavior: when the button is pressed with user the button's text and the label's text should are changed together.
But the problem is button and label have the equal name of keys for the text properties. And I can't store equal keys in one hash-map.
;;I have two atoms keeps the state of button text and state of label text
(def *button-text (atom
{:text "click me"}))
(def *label-text (atom
{:text "press the button"}))
;;I have the root function which should accepts arguments for button and label props.
But these props have equal names - text - and I can't store two equal keys in one map.
{:fx/type root
:text (:text *button-text)
:text (:text *label-text)}
;;This will cause an error.
This is how I solved this problem. But is too much of the code and out of normal way.
(ns examp.core
(:gen-class)
(:require [cljfx.api :as fx])
(:import [javafx.application Platform]))
(def *button-text (atom
{:text "click me"}))
(def *label-text (atom
{:text "press the button"}))
(def renderer (fx/create-renderer))
(defn root [{:keys [one two]}]
(let [button-text (:text one)
label-text (:text two)]
{:fx/type :stage
:showing true
:title "Window"
:width 250
:height 150
:scene {:fx/type :scene
:root {:fx/type :v-box
:alignment :center
:spacing 10
:children [{:fx/type :label
:text label-text}
{:fx/type :button
:min-width 100
:min-height 50
:text button-text
:on-action (fn [_]
(if (= button-text "click me")
(do
(swap! *button-text assoc :text "clicked")
(swap! *label-text assoc :text "button is pressed")
(renderer
{:fx/type root
:one #*button-text
:two #*label-text}))
(do
(swap! *button-text assoc :text "click me")
(swap! *label-text assoc :text "presse the button")
(renderer
{:fx/type root
:one #*button-text
:two #*label-text}))))}]}}}))
(defn -main [& args]
(Platform/setImplicitExit true)
(renderer {:fx/type root
:one #*button-text
:two #*label-text}))
Before I will show you my attempt, here is the link to the official Cljfx examples repository. These examples should be useful for you, as they show practices for managing app state, event handling and so on.
For this situation, I recommend studying e05_fn_fx_like_state.clj- this is also an example I based my code on:
(ns examp.core
(:require [cljfx.api :as fx])
(:gen-class))
(def *state
(atom {:label-text "Click the button."
:button-text "Click me!"}))
(defn root [{:keys [label-text button-text]}]
{:fx/type :stage
:showing true
:title "Window"
:width 250
:height 150
:scene {:fx/type :scene
:root {:fx/type :v-box
:alignment :center
:spacing 10
:children [{:fx/type :label
:text label-text}
{:fx/type :button
:text button-text
:min-width 100
:min-height 50
:on-action {:key :button-action}}]}}})
(defn map-event-handler [event]
(when (= (:key event) :button-action)
(if (= (:button-text #*state) "Click me!")
(reset! *state {:label-text "The button was clicked!"
:button-text "Clicked!"})
(reset! *state {:label-text "Click the button."
:button-text "Click me!"}))))
(def renderer
(fx/create-renderer
:middleware (fx/wrap-map-desc root)
:opts {:fx.opt/map-event-handler map-event-handler}))
(defn -main [& args]
(fx/mount-renderer *state renderer))

Getting error Uncaught Error: Assert failed: Reaction is read only; on-set is not allowed

I am new to clojure and reagent. I was trying to generate dynamic number of checkboxes, the state of which is stored in the app state which is a list of dicts like this
[{:checked false, :text "Sample text 1"} {:checked false, :text "Sample text 2"} {:checked false, :text "Sample text 3"}]
The function below is expected to generate a checkbox corresponding to the specified index of app's db (db). The function does it jobs and the checkboxes are clickable.
(defn gen-checkbox [index db]
[re-com/checkbox
:label (:text (#db index))
:model (:checked (#db index))
:on-change #(swap! db assoc-in [index :checked] (not(:checked (#db index))))
])
However, I get this error in the browser console when I click on any checkbox.
Uncaught Error: Assert failed: Reaction is read only; on-set is not allowed
The error occurs at swap!. Can some one point out what I am doing wrong?
The db initialization part is as below:
(re-frame/reg-event-db ::initialize-db
(fn [_ _]
(atom [{:checked false :text "Sample text"}, {:checked false :text "Sample text"}, {:checked false :text "Sample text"}])
))
I also have a function to retreive the db. I am currently getting
(re-frame/reg-sub ::getdb
(fn [db]
#db
))
Based on the tags of your question, I presume that you are using re-frame.
You can't update the database in re-frame directly. Instead, you should register an event handler that updates the database, like the below (the exact code depends on the structure of your DB):
;; (require '[re-frame.core :as rf])
(rf/reg-event-db
:toggle-checkbox
(fn [db [_ index]]
(update-in db [index :checked] not)))
And then dispatch the event in the code of your checkbox's renderer:
...
:on-change #(rf/dispatch [:toggle-checkbox index])
...

How to get the dialog functions work

I want to open a dialog, and work with the data after the the dialog returns ok. The problem is that :success-fn is not called after the dialog is submitted. That has something to do with the listener from the button. If the connectDialog is called without listener, the function of :success-fn is called.
Code:
(def dbConnectionForm
(grid-panel :columns 2
:items ["Database Driver" (combobox :id :dbdriver :model ["postgresql" "mysql"])
"Database" (text :id :dbname :text "postgres")
"Port" (text :id :dbport :text "32768")
"Username" (text :id :username :text "postgres")
"Password" (text :id :password :text "postgres")]))
(defn connectionDialog []
(print (-> (dialog
:content dbConnectionForm
:option-type :ok-cancel
:type :plain
:success-fn (fn [e] (print (value dbConnectionForm)))
)pack! show!))
)
(def connectButton (button :text "Connect"
:listen [:action (fn [e] (connectionDialog))]))
This is probably because you're using print. Change it to println or add a call to flush inside the callback after the print.
If (value dbConnectionForm) returns a small value (as in something that when turned into a String has only a few characters), and doesn't contain newlines, it might not prompt the outstream to automatically flush, so the text gets stuck in the buffer.

Reagent-Forms radio buttons displaying as text fields

I am trying to display a group of radio buttons in a reagent/cljs app. I have followed the same process from http://yogthos.github.io/reagent-forms-example.html but the radio buttons I am displaying are showing up as textfield input boxes.
(def ^:private options (atom nil))
(defn set-options []
(reset! options
[{:name "label name"}
{:name "label name"}
{:name "label name"}]))
(defn set-radio-buttons []
(set-options)
(for [option #options]
[:div.radio
[:label
[:input {:field :radio}]
(option :name)]]))
(defn response-box []
[:div#response
(set-radio-buttons)])
I am then placing response-box in the ui layer of the app.
Thanks
Field is not a correct input element attribute.
[:input {:field :radio}]
(option :name)]]))
Should probably be
[:input {:type :radio}]
(option :name)]]))

Clojure Seesaw make menu of radio-menu items out of a list of files

I have a list of files like so ("File1" "File2" "File3")
I want to turn this into a of radio-menu-items
like this
(menu :text "Lists" :items [(radio-menu-item :text "File 1")(radio-menu-item :text "File 2")(radio-menu-item :text "File 3")])
I have tried looping like this
(def Radios (for [ item '("File1" "File2" "File3")] (radio-menu-item :text item)))
but this does not work.
How can this be accomplished?
your use of a for expression looks correct, if I change the expression slightly so it returns the list it would run instead of running it we can verify this:
core> (list 'menu :text "Lists" :items
(vec (for [ item '("File1" "File2" "File3")]
(list 'radio-menu-item :text item))))
(menu :text "Lists" :items [(radio-menu-item :text "File1")
(radio-menu-item :text "File2")
(radio-menu-item :text "File3")])
so the finished expression becomes:
core> (menu :text "Lists" :items
(vec (for [ item '("File1" "File2" "File3")]
(radio-menu-item :text item))))
provided that menu and radio-menu-item resolve to the proper values.