1
+ from qtpy .QtCore import Signal , QSortFilterProxyModel , Qt
2
+ from qtpy .QtSql import QSqlTableModel , QSqlDatabase , QSqlQuery
3
+ from qtpy .QtWidgets import QApplication , QWidget , QVBoxLayout , QMessageBox , QStyledItemDelegate , QTableView , QAbstractItemView , \
4
+ QHBoxLayout , \
5
+ QLabel , QSpacerItem , QSizePolicy , QFileDialog , QComboBox
6
+
7
+ # for search feature
8
+ from pyqt_openai .pyqt_openai_data import DB
9
+ from pyqt_openai .widgets .button import Button
10
+ from pyqt_openai .widgets .searchBar import SearchBar
11
+
12
+
13
+ class FilterProxyModel (QSortFilterProxyModel ):
14
+ def __init__ (self ):
15
+ super ().__init__ ()
16
+ self .__searchedText = ''
17
+
18
+ @property
19
+ def searchedText (self ):
20
+ return self .__searchedText
21
+
22
+ @searchedText .setter
23
+ def searchedText (self , value ):
24
+ self .__searchedText = value
25
+ self .invalidateFilter ()
26
+
27
+
28
+ # for align text in every cell to center
29
+ class AlignDelegate (QStyledItemDelegate ):
30
+ def initStyleOption (self , option , index ):
31
+ super ().initStyleOption (option , index )
32
+ option .displayAlignment = Qt .AlignmentFlag .AlignCenter
33
+
34
+
35
+ class SqlTableModel (QSqlTableModel ):
36
+ added = Signal (int , str )
37
+ updated = Signal (int , str )
38
+ deleted = Signal (list )
39
+ addedCol = Signal ()
40
+ deletedCol = Signal ()
41
+
42
+ def flags (self , index ):
43
+ if index .column () == self .column_index_by_name ('name' ):
44
+ return Qt .ItemFlag .ItemIsEnabled | Qt .ItemFlag .ItemIsSelectable | Qt .ItemFlag .ItemIsEditable
45
+ return Qt .ItemFlag .ItemIsEnabled | Qt .ItemFlag .ItemIsSelectable
46
+
47
+ def column_index_by_name (self , name ):
48
+ return self .fieldIndex (name )
49
+
50
+
51
+ class ChatNavWidget (QWidget ):
52
+ added = Signal ()
53
+ clicked = Signal (int , str )
54
+ cleared = Signal ()
55
+ onImport = Signal (str )
56
+ onExport = Signal (list )
57
+
58
+ def __init__ (self , columns , table_nm ):
59
+ super ().__init__ ()
60
+ self .__initVal (columns , table_nm )
61
+ self .__initUi ()
62
+
63
+ def __initVal (self , columns , table_nm ):
64
+ self .__columns = columns
65
+ self .__table_nm = table_nm
66
+
67
+ def __initUi (self ):
68
+ # Set up the database and table model (you'll need to configure this part based on your database)
69
+ self .__imageDb = QSqlDatabase .addDatabase ('QSQLITE' ) # Replace with your database type
70
+ self .__imageDb .setDatabaseName ('conv.db' ) # Replace with your database name
71
+ self .__imageDb .open ()
72
+
73
+ imageGenerationHistoryLbl = QLabel ()
74
+ imageGenerationHistoryLbl .setText ('History' )
75
+
76
+ self .__addBtn = Button ()
77
+ self .__delBtn = Button ()
78
+ self .__importBtn = Button ()
79
+ self .__saveBtn = Button ()
80
+ self .__clearBtn = Button ()
81
+
82
+ self .__addBtn .setStyleAndIcon ('ico/add.svg' )
83
+ self .__delBtn .setStyleAndIcon ('ico/delete.svg' )
84
+ self .__importBtn .setStyleAndIcon ('ico/import.svg' )
85
+ self .__saveBtn .setStyleAndIcon ('ico/save.svg' )
86
+ self .__clearBtn .setStyleAndIcon ('ico/close.svg' )
87
+
88
+ self .__addBtn .setToolTip ('Add' )
89
+ self .__delBtn .setToolTip ('Delete' )
90
+ self .__importBtn .setToolTip ('SQLite DB Import (Working)' )
91
+ self .__saveBtn .setToolTip ('Save' )
92
+ self .__delBtn .setToolTip ('Remove All' )
93
+
94
+ self .__addBtn .clicked .connect (self .add )
95
+ self .__delBtn .clicked .connect (self .__delete )
96
+ self .__importBtn .clicked .connect (self .__import )
97
+ self .__saveBtn .clicked .connect (self .__export )
98
+ self .__clearBtn .clicked .connect (self .__clear )
99
+
100
+ lay = QHBoxLayout ()
101
+ lay .addWidget (imageGenerationHistoryLbl )
102
+ lay .addSpacerItem (QSpacerItem (10 , 10 , QSizePolicy .Policy .MinimumExpanding ))
103
+ lay .addWidget (self .__addBtn )
104
+ lay .addWidget (self .__delBtn )
105
+ lay .addWidget (self .__clearBtn )
106
+ lay .addWidget (self .__importBtn )
107
+ lay .addWidget (self .__saveBtn )
108
+ lay .setContentsMargins (0 , 0 , 0 , 0 )
109
+
110
+ menuSubWidget1 = QWidget ()
111
+ menuSubWidget1 .setLayout (lay )
112
+
113
+ self .__searchBar = SearchBar ()
114
+ self .__searchBar .setPlaceHolder ('Search...' )
115
+ self .__searchBar .searched .connect (self .__search )
116
+
117
+ self .__searchOptionCmbBox = QComboBox ()
118
+ self .__searchOptionCmbBox .addItems (['Title' , 'Content' ])
119
+ self .__searchOptionCmbBox .setMinimumHeight (self .__searchBar .sizeHint ().height ())
120
+ self .__searchOptionCmbBox .currentIndexChanged .connect (
121
+ lambda _ : self .__search (self .__searchBar .getSearchBar ().text ()))
122
+
123
+ lay = QHBoxLayout ()
124
+ lay .addWidget (self .__searchBar )
125
+ lay .addWidget (self .__searchOptionCmbBox )
126
+ lay .setContentsMargins (0 , 0 , 0 , 0 )
127
+
128
+ menuSubWidget2 = QWidget ()
129
+ menuSubWidget2 .setLayout (lay )
130
+
131
+ lay = QVBoxLayout ()
132
+ lay .addWidget (menuSubWidget1 )
133
+ lay .addWidget (menuSubWidget2 )
134
+ lay .setContentsMargins (0 , 0 , 0 , 0 )
135
+
136
+ menuWidget = QWidget ()
137
+ menuWidget .setLayout (lay )
138
+
139
+ self .__model = SqlTableModel (self )
140
+ self .__model .setTable (self .__table_nm )
141
+ self .__model .beforeUpdate .connect (self .__updated )
142
+
143
+ for i in range (len (self .__columns )):
144
+ self .__model .setHeaderData (i , Qt .Orientation .Horizontal , self .__columns [i ])
145
+ self .__model .select ()
146
+ # descending order by insert date
147
+ idx = self .__columns .index ('insert_dt' )
148
+ self .__model .sort (idx , Qt .SortOrder .DescendingOrder )
149
+
150
+ # init the proxy model
151
+ self .__proxyModel = FilterProxyModel ()
152
+
153
+ # set the table model as source model to make it enable to feature sort and filter function
154
+ self .__proxyModel .setSourceModel (self .__model )
155
+
156
+ # set up the view
157
+ self .__tableView = QTableView ()
158
+ self .__tableView .setModel (self .__proxyModel )
159
+ self .__tableView .setEditTriggers (QTableView .EditTrigger .DoubleClicked | QTableView .EditTrigger .SelectedClicked )
160
+ self .__tableView .setSortingEnabled (True )
161
+
162
+ # align to center
163
+ delegate = AlignDelegate ()
164
+ for i in range (self .__model .columnCount ()):
165
+ self .__tableView .setItemDelegateForColumn (i , delegate )
166
+
167
+ # set selection/resize policy
168
+ self .__tableView .setSelectionBehavior (QAbstractItemView .SelectionBehavior .SelectRows )
169
+ self .__tableView .setSelectionMode (QAbstractItemView .SelectionMode .ExtendedSelection )
170
+
171
+ self .__tableView .clicked .connect (self .__clicked )
172
+ self .__tableView .activated .connect (self .__clicked )
173
+
174
+ lay = QVBoxLayout ()
175
+ lay .addWidget (menuWidget )
176
+ lay .addWidget (self .__tableView )
177
+ self .setLayout (lay )
178
+
179
+ self .refreshData ()
180
+
181
+ def add (self , called_from_parent = False ):
182
+ if called_from_parent :
183
+ pass
184
+ else :
185
+ self .added .emit ()
186
+ self .__model .select ()
187
+
188
+ def __import (self ):
189
+ filename = QFileDialog .getOpenFileName (self , 'Import' , '' , 'SQLite DB files (*.db)' )
190
+ if filename :
191
+ filename = filename [0 ]
192
+ self .onImport .emit (filename )
193
+
194
+ def __export (self ):
195
+ self .onExport .emit (self .__getSelectedIds ())
196
+
197
+ def __updated (self , i , r ):
198
+ # send updated signal
199
+ self .__model .updated .emit (r .value ('id' ), r .value ('name' ))
200
+
201
+ def refreshData (self , title = None ):
202
+ self .__model .select ()
203
+ # index -1 will be read from all columns
204
+ # otherwise it will be read the current column number indicated by combobox
205
+ self .__proxyModel .setFilterKeyColumn (- 1 )
206
+ # regular expression can be used
207
+ self .__proxyModel .setFilterRegularExpression (title )
208
+
209
+ def __clicked (self , idx ):
210
+ # get id of record
211
+ id = self .__model .data (self .__proxyModel .mapToSource (idx .siblingAtColumn (0 )), role = Qt .ItemDataRole .DisplayRole )
212
+ title = self .__model .data (self .__proxyModel .mapToSource (idx .siblingAtColumn (1 )), role = Qt .ItemDataRole .DisplayRole )
213
+
214
+ self .clicked .emit (id , title )
215
+
216
+ def __getSelectedIds (self ):
217
+ idx_s = [idx .siblingAtColumn (0 ) for idx in self .__tableView .selectedIndexes ()]
218
+ idx_s = list (set (idx_s ))
219
+ ids = [self .__model .data (idx , role = Qt .ItemDataRole .DisplayRole ) for idx in idx_s ]
220
+ return ids
221
+
222
+ def __delete (self ):
223
+ ids = self .__getSelectedIds ()
224
+ for _id in ids :
225
+ DB .deleteConv (_id )
226
+ self .__model .select ()
227
+ self .cleared .emit ()
228
+
229
+ def __clear (self ):
230
+ '''
231
+ Clear all data in the table
232
+ '''
233
+ # Before clearing, confirm the action
234
+ reply = QMessageBox .question (self , 'Confirm' , 'Are you sure to clear all data?' , QMessageBox .StandardButton .Yes | QMessageBox .StandardButton .No )
235
+ if reply == QMessageBox .StandardButton .Yes :
236
+ DB .deleteConv ()
237
+ self .__model .select ()
238
+ self .cleared .emit ()
239
+
240
+ def __search (self , search_text ):
241
+ # title
242
+ if self .__searchOptionCmbBox .currentText () == 'Title' :
243
+ self .refreshData (search_text )
244
+ # content
245
+ elif self .__searchOptionCmbBox .currentText () == 'Content' :
246
+ if search_text :
247
+ convs = DB .selectAllContentOfConv (content_to_select = search_text )
248
+ ids = [_ [0 ] for _ in convs ]
249
+ self .__model .setQuery (QSqlQuery (f"SELECT { ',' .join (self .__columns )} FROM { self .__table_nm } "
250
+ f"WHERE id IN ({ ',' .join (map (str , ids ))} )" ))
251
+ else :
252
+ self .refreshData ()
253
+
254
+ def isCurrentConvExists (self ):
255
+ return self .__model .rowCount () > 0 and self .__tableView .currentIndex ()
256
+
257
+
258
+ if __name__ == "__main__" :
259
+ import sys
260
+
261
+ app = QApplication (sys .argv )
262
+ w = ChatNavWidget (['id' , 'name' , 'update_dt' , 'insert_dt' ], 'conv_tb' )
263
+ w .show ()
264
+ sys .exit (app .exec ())
0 commit comments