Меnu:


The application that today we will port to commonQt is a note management application, the application allows us to create notes and then easily search any term or keyword within the set of notes created. This application uses sqlite as the backend database.

Why commonQt and not just Qt?.

Risc is the future. It is a phrase from the movie Hackers of 1995. Well, several years ago we have ARM that is a sprout from Risc. We started in 2007 with the nokia n800 tablet, three years ago we moved to the Nokia n900 phone and a couple of years ago we have available an always-on machine that is the orange pi. One of the applications that we have not yet been able to migrate from x86 to Arm is the dbdesigner-fork. It is for modeling databases It have been developed in kylix (delphi for linux), it has been many years since the kylix have been abandon-ware. However the dbdesigner application is still working, therefore we continue using it. There have been attempts to migrate it to Lazarus but without success until today. And since our favorite text editor is Emacs, we thought it would be a good opportunity for making a small application in commonQt with the goal of coding an alternative to dbdesigner in commonQt in the near future . Due to the size of the dbdesigner , we expect commonQt help us accelerate the development. We'll see how it goes.

What do we will do?

We are going to port the app made in wxwidgets to commonQt . We will use the Qt designer in ArchLinux to design the user interface.

Previously we have written about how to make the application in 2 articles:

  1. missing wxwidgets sample.
  2. Missing wxwidgets example part 2.

Showing the App coded in wxwidgets

Showing the common-lisp code




(require 'qt)
(in-package :common-lisp-user)

(defpackage "PEAPP-QT"
  (:use cl qt clsql-sqlite3)
  (:export "TIMELINE-MAIN"))

(in-package :peapp-qt)
(defvar *entity-note* nil)
(named-readtables:in-readtable :qt)

(defun find-child (object name)
  (let ((children (#_children object)))
    (or
     (loop for child in children
           when (equal name (#_objectName child))
           return child)
     (loop for child in children
           thereis (find-child child name)))))

(defclass timeline-mainform ()
  (
   (tableWidget :accessor tableWidget)
   (lineEdit :accessor lineEdit)
   (detailDialog :accessor detailDialog)
   )
  (:metaclass qt-class)
  (:qt-superclass "QWidget")
  (:slots ("timeline-mainform-show-dialog-detail()" timeline-mainform-show-dialog-detail)
          ("timeline-mainform-selected-item()" timeline-mainform-selected-item)
          ("timeline-mainform-on-add()" timeline-mainform-on-add)
          ("timeline-mainform-on-delete()" timeline-mainform-on-delete)
          ;("textChanged(QString)" timeline-mainform-line-edit-text-changed)
          ("timeline-mainform-line-edit-text-changed()" timeline-mainform-line-edit-text-changed))
  (:override ("closeEvent" timeline-mainform-close-event)))

(defmethod initialize-instance :after ((instance timeline-mainform) &key)
  (new instance)
  (#_setWindowTitle instance "timeline on commonQt")
      (with-objects ((file (#_new QFile "/home/olla/dev/cl/timeline/timeline_main_form_widget.ui"))
                     (loader (#_new QUiLoader)))
                    (if (#_open file 1)
                        (let ((win (#_load loader file instance))
                              (layout (#_new QVBoxLayout))
                              )
                          (#_close file)
                          (#_addWidget layout win)
                          (#_setLayout instance layout)
                          (setf (tableWidget instance) (find-child win "tableWidget"))
                          (setf (lineEdit instance) (find-child win "lineEdit"))
                          (connect (find-child win "pushButton") "clicked()" instance "timeline-mainform-on-add()");add
                          (connect (find-child win "pushButton_2") "clicked()" instance "close()");cancel
                          (connect (find-child win "pushButton_3") "clicked()" instance "timeline-mainform-on-delete()");del
                          ;(connect (find-child win "pushButton_4") "clicked()" instance "timeline-mainform-on-ok()");ok
                          (connect (find-child win "tableWidget") "cellActivated(int, int)" instance "timeline-mainform-selected-item()")
                          ;(connect (find-child win "lineEdit") "textChanged(QString)" instance "textChanged(QString)") ; just widget value
                          (connect (find-child win "lineEdit") "textChanged(QString)" instance "timeline-mainform-line-edit-text-changed()")
                          (timeline-mainform-set-ui-properties instance)
                          (timeline-mainform-ui-load-data instance)
                          )
                      (error (concatenate 'string "Couldn't open " "filename" " file!") ))))



;(defun load-data(instance) (was as recalc on aproxgnuplot.lisp)

(defmethod timeline-mainform-set-ui-properties ((instance timeline-mainform))
    (#_setColumnCount (tableWidget instance) 2)
    (#_setHorizontalHeaderLabels (tableWidget instance) '("name" ""))
    (#_setResizeMode (#_horizontalHeader (tableWidget instance)) 0 (#_QHeaderView::Stretch))
                                        ; do not show numbers on rows
  )

(defmethod timeline-mainform-ui-load-data ((instance timeline-mainform))
  (let ((item)
        (count)
        (row-index 0)
        (search-input (string-trim " " (#_text (lineEdit instance))))
        (rows))
    (setq rows (get-all-my-notes-from-db search-input))
    (#_clearContents (tableWidget instance))
    (setq count (length rows))
    (#_setRowCount (tableWidget instance) count)
    (with-slots (tableWidget) instance
      (dolist (item-row rows)
        (when item-row
          (setf item (#_new QTableWidgetItem (cadr item-row)))
          (#_setItem tableWidget row-index 0 item)
          (setf item (#_new QTableWidgetItem (write-to-string (car item-row))))
          (#_setItem tableWidget row-index 1 item)
          (incf row-index)
          )
      ))
    )
  )

(defmethod timeline-mainform-close-event ((instance timeline-mainform) close-event)
  (clsql:disconnect)
  (#_accept close-event))

(defmethod timeline-mainform-show-dialog-detail ((instance timeline-mainform))
  (#_show (detailDialog instance))
  )


(defmethod timeline-mainform-selected-item ((instance timeline-mainform))
  (let ((item (if (#_item (tableWidget instance) (#_currentRow (tableWidget instance)) 1) (#_item (tableWidget instance) (#_currentRow (tableWidget instance)) 1) nil)))
    (when item
      (if (not *entity-note*)
          (setf *entity-note* (make-instance 'entity-note :ui-id (#_text item)))
          (setf (ui-id *entity-note*) (#_text item))
          )
      (load-from-db *entity-note*)
      ; load entity-data on dialog
      (#_setDate (date-obj (detailDialog instance)) (#_QDate::fromString (date *entity-note*) "yyyy-MM-dd"))
      (#_setText (title-obj (detailDialog instance)) (title *entity-note*))
      (#_setText (content-obj (detailDialog instance)) (description *entity-note*))
      ; set read-only the date nil is false (not nil) is true
      (#_setEnabled (date-obj (detailDialog instance)) nil)
      (#_show (detailDialog instance))
      )
    )
  )

(defmethod timeline-mainform-on-add ((instance timeline-mainform))

  (if (not *entity-note*)
      (setf *entity-note* (make-instance 'entity-note :ui-id nil :title "" :description ""))
      (progn
        (setf (ui-id *entity-note*) nil)
        (setf (title *entity-note*) "")
        (setf (date *entity-note*) nil)
        (setf (description *entity-note*) "")
        )
      )
  (#_setDate (date-obj (detailDialog instance)) (#_QDate::currentDate))
  (#_setText (title-obj (detailDialog instance)) (title *entity-note*))
  (#_setText (content-obj (detailDialog instance)) (description *entity-note*))
  (#_setEnabled (date-obj (detailDialog instance)) (not nil))
  (#_show (detailDialog instance))
  )

(defmethod timeline-mainform-on-delete ((instance timeline-mainform))

  (let (
        (mylist (#_selectedIndexes (tableWidget instance) ))
        (mycount)
        (my-row-index)
        (myitem)
        (my-yes-no-var)
        (result-yes (enum-value (#_QMessageBox::Yes)))
        )
    (when mylist
      (setq mycount (length mylist))
      (when (= (- mycount 1) 0)
        (setq my-row-index (#_row (car mylist)))
                                        ; get item
        (setq myitem (#_item (tableWidget instance) my-row-index 1))
        (when myitem
          (setq my-yes-no-var (#_QMessageBox::question instance "delete record" "Sure, about deletion?" (#_QMessageBox::Yes) (#_QMessageBox::No)))
          (when (= (enum-value my-yes-no-var)  result-yes)
            (if (not *entity-note*)
                (setf *entity-note* (make-instance 'entity-note :ui-id (#_text myitem)))
                (setf (ui-id *entity-note*) (#_text myitem))
                )
            (delete-from-db *entity-note*)
            ))))))

;(defun text-changed (widget value)
  ;(setf (value widget) (parse-date value)))
;(defun timeline-mainform-line-edit-text-changed (widget value)
(defmethod timeline-mainform-line-edit-text-changed  ((instance timeline-mainform))
                                        ;(print (concatenate 'string "timeline-mainform-text-changed0=" value))
  (let (
        (input (string-trim " " (#_text (lineEdit instance))))
        )
    (when (or (= (length input) 0) (> (length input) 0))
      (timeline-mainform-ui-load-data instance)
      )
    )
  )

(defun get-all-my-notes-from-db (search-input)
  (let ((sql-query "SELECT id, date(date) || ', ' || title FROM time_lines"))
    (if (> (length search-input) 0)
        (setq sql-query
              (concatenate 'string
                           sql-query
                           " where title like '%"
                           search-input
                           "%' or "
                           " description like '%"
                           search-input
                           "%' or "
                           " id like '%"
                           search-input
                           "%' "
              )
        ))
    (setq sql-query (concatenate 'string
                                 sql-query
                                 " order by date desc"))
    (clsql:query sql-query)
    )
  )

(defun get-note-from-db (id-param)
  (clsql:query
   (concatenate 'string "SELECT date(date), title, description FROM time_lines where id=" id-param))
  )

(defclass note-dialog ()
  ((date-obj :accessor date-obj)
   (title-obj :accessor title-obj)
   (content-obj :accessor content-obj)
   (parentFrame :accessor parentFrame)
   )
  (:metaclass qt-class)
  (:qt-superclass "QDialog")
  (:slots
   ("updateEntityObject()" note-dialog-update-entity-object))
  )


(defmethod initialize-instance :after ((window note-dialog) &key)
  (new window)
  (#_setWindowTitle window "Mis detalles")
  (let ((layout (#_new QFormLayout))
        (date-edit (#_new QDateEdit))
        (input (#_new QLineEdit  ""))
        (text-edit (#_new QTextEdit))
        (buttons (#_new QDialogButtonBox
                        (enum-or
                         (#_QDialogButtonBox::Ok)
                         (#_QDialogButtonBox::Cancel))
                        (#_Qt::Horizontal)))
        )
    (setf (date-obj window)  date-edit)
    (setf (title-obj window)  input)
    (setf (content-obj window)  text-edit)
    (#_setLayout window layout)
    (#_addWidget layout date-edit)
    (#_addWidget layout input)
    (#_addWidget layout text-edit)
    (#_addWidget layout buttons)
    (connect buttons "accepted()"
             window "updateEntityObject()")
    (connect buttons "accepted()"
             window "accept()")
    (connect buttons "rejected()"
             window "reject()"))
  )

(defmethod note-dialog-update-entity-object ((instance note-dialog))
  (setf (title *entity-note*) (#_text (title-obj instance)))
  (setf (date *entity-note*) (#_toString (#_date (date-obj instance) ) "yyyy-MM-dd"))
  (setf (description *entity-note*) (#_toPlainText (content-obj instance)))
                                        ; save object to db
  (if (ui-id *entity-note*)
      (update-to-db *entity-note*)
      (insert-to-db *entity-note*)
      )
                                        ; update list ui with the changes (chk wxwidgets)
  (timeline-mainform-ui-load-data  (parentFrame instance))
  )

(defclass entity-note ()
  ((ui-id :accessor ui-id
          :initarg  :ui-id)
   (title :accessor title
          :initarg :title)
   (description :accessor description
                :initarg :description)
   (date :accessor date)
   ))

(defmethod load-from-db ((entity entity-note))
  (let (
        (rows (get-note-from-db (ui-id entity))))
    (dolist (item-row rows)
      (setf (title entity) (cadr item-row))
      (setf (date entity) (car item-row))
      (setf (description entity) (cadr (cdr item-row)))
      )
    ;(print (format t " load-from-db ~A" (description defmethod)))
    )
  )

(defmethod insert-to-db ((entity entity-note))
  (if (not (ui-id entity))
      (clsql:execute-command (concatenate 'string "insert into time_lines (date, title, description, created_at) values (date('" (date entity) "'), '" (title entity) "', '" (description entity) "', datetime('now', 'localtime') )"))
      )
  )

(defmethod update-to-db ((entity entity-note))
  (if (ui-id entity)
      (clsql:execute-command (concatenate 'string "UPDATE time_lines set title='" (title entity) "', description='" (description entity) "', updated_at=datetime('now', 'localtime') where id=" (ui-id entity)))
      )
  )

(defmethod delete-from-db ((entity entity-note))
  (if (ui-id entity)
      (clsql:execute-command (concatenate 'string "delete from time_lines where id=" (ui-id entity)))
      )
  )

(defun timeline-main()
  (clsql:connect '("/home/olla/.wxtimeline/wx_timelines.sqlite3") :database-type :sqlite3)
  (qt:ensure-smoke "qtuitools")
  (make-qapplication)
  ;(make-qapplication "-display" "localhost:10.0") ;(*qapplication* var created just once)
  (with-objects (
                 (mainform (make-instance 'timeline-mainform))
                 (dialog_detail (make-instance 'note-dialog))
                 )
    (setf (detailDialog mainform) dialog_detail)
    (setf (parentFrame dialog_detail) mainform)
    (#_show mainform)
    (#_exec *qapplication*)))

Showing the UI design file on designer-qt4

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>806</width>
    <height>475</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <layout class="QFormLayout" name="formLayout">
     <item row="0" column="0">
      <widget class="QLabel" name="label">
       <property name="text">
        <string>&amp;Search</string>
       </property>
      </widget>
     </item>
     <item row="0" column="1">
      <widget class="QLineEdit" name="lineEdit"/>
     </item>
    </layout>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout"/>
   </item>
   <item>
    <widget class="QTableWidget" name="tableWidget">
     <property name="columnCount">
      <number>3</number>
     </property>
     <attribute name="horizontalHeaderDefaultSectionSize">
      <number>100</number>
     </attribute>
     <column>
      <property name="text">
       <string>uno</string>
      </property>
     </column>
     <column>
      <property name="text">
       <string>dos</string>
      </property>
     </column>
     <column>
      <property name="text">
       <string>tres</string>
      </property>
     </column>
    </widget>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout_2">
     <item>
      <widget class="QPushButton" name="pushButton">
       <property name="text">
        <string>&amp;Add</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_3">
       <property name="text">
        <string>&amp;Del</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_4">
       <property name="text">
        <string>&amp;Ok</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_2">
       <property name="text">
        <string>&amp;Cancel</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

Showing the database creation script

echo "CREATE TABLE IF NOT EXISTS time_lines (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, title varchar(255), description text, date date, created_at datetime, updated_at datetime);" | sqlite3 wx_timelines.sqlite3

Showing the App on CommonQt

This one is similar to the one we have shown above.

This image is for a record detail

Conclusion

There are few examples of applications in commonQt . We imagine that this will be one of the few blog posts about the subject this year.

Last change: 21.12.2018 07:01

blog comments powered by Disqus