q2rad 0.1.202__tar.gz → 0.1.204__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of q2rad might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: q2rad
3
- Version: 0.1.202
3
+ Version: 0.1.204
4
4
  Summary: RAD - database, GUI, reports
5
5
  Author: Andrei Puchko
6
6
  Author-email: andrei.puchko@gmx.de
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "q2rad"
3
- version = "0.1.202"
3
+ version = "0.1.204"
4
4
  description = "RAD - database, GUI, reports"
5
5
  authors = ["Andrei Puchko <andrei.puchko@gmx.de>"]
6
6
  readme = "README.md"
@@ -20,7 +20,7 @@ from q2gui.q2dialogs import q2AskYN, q2Mess, Q2WaitShow, q2ask, q2working
20
20
 
21
21
  from q2rad import Q2Form
22
22
  from q2terminal.q2terminal import Q2Terminal
23
- from q2rad.q2raddb import today, insert, update, get
23
+ from q2rad.q2raddb import insert, update, get
24
24
  from datetime import datetime
25
25
 
26
26
  import json
@@ -108,7 +108,7 @@ class AppManager(Q2Form):
108
108
  self.add_control(
109
109
  "drl",
110
110
  "Database type",
111
- data=app_data["driver_logic"],
111
+ data=app_data["driver_logic"].lower(),
112
112
  disabled=1,
113
113
  datalen=len(app_data["driver_logic"].strip()) + 5,
114
114
  )
@@ -186,7 +186,7 @@ class AppManager(Q2Form):
186
186
  self.add_control(
187
187
  "drd",
188
188
  "Database type",
189
- data=app_data["driver_data"],
189
+ data=app_data["driver_data"].lower(),
190
190
  disabled=1,
191
191
  datalen=len(app_data["driver_data"].strip()) + 5,
192
192
  )
@@ -15,8 +15,8 @@
15
15
 
16
16
  from q2gui import q2app
17
17
  from q2gui.q2form import NEW, COPY
18
- from q2gui.q2model import Q2CursorModel
19
- from q2gui.q2dialogs import q2Mess, q2Wait, q2mess, q2working
18
+ from q2gui.q2dialogs import q2Mess
19
+ from q2gui.q2utils import Q2Crypto
20
20
 
21
21
  from q2db.schema import Q2DbSchema
22
22
  from q2db.db import Q2Db
@@ -33,7 +33,7 @@ from q2rad.q2appmanager import AppManager
33
33
  from q2rad.q2raddb import open_url
34
34
 
35
35
 
36
- SQL_ENGINES = ["MySQl", "Sqlite", "Postgresql"]
36
+ SQL_ENGINES = ["MySQL", "SQLite", "PostgreSQL"]
37
37
 
38
38
  _ = gettext.gettext
39
39
 
@@ -81,11 +81,6 @@ class Q2AppSelect(Q2Form):
81
81
  self.add_control("/")
82
82
  if self.add_control("/f", _("Data storage")):
83
83
 
84
- def driverDataValid(self=self):
85
- self.w.host_data.set_enabled(self.s.driver_data != "Sqlite")
86
- self.w.port_data.set_enabled(self.s.driver_data != "Sqlite")
87
- self.w.select_data_storage_file.set_enabled(self.s.driver_data == "Sqlite")
88
-
89
84
  self.add_control(
90
85
  "driver_data",
91
86
  label=_("Storage type"),
@@ -94,7 +89,7 @@ class Q2AppSelect(Q2Form):
94
89
  datatype="char",
95
90
  datalen=30,
96
91
  pic=";".join(SQL_ENGINES),
97
- valid=driverDataValid,
92
+ valid=self.driver_data_valid,
98
93
  )
99
94
  if self.add_control("/h"):
100
95
  self.add_control(
@@ -109,7 +104,6 @@ class Q2AppSelect(Q2Form):
109
104
  _("?"),
110
105
  mess=_("Open Data Storage sqlite database file"),
111
106
  control="button",
112
- datalen=3,
113
107
  valid=self.openSqliteDataFile,
114
108
  )
115
109
  self.add_control("/")
@@ -130,11 +124,6 @@ class Q2AppSelect(Q2Form):
130
124
 
131
125
  if self.add_control("/f", _("Logic storage")):
132
126
 
133
- def driverLogicValid(form=self):
134
- form.w.host_logic.set_enabled(form.s.driver_logic != "Sqlite")
135
- form.w.port_logic.set_enabled(form.s.driver_logic != "Sqlite")
136
- form.w.select_app_storage_file.set_enabled(form.s.driver_logic == "Sqlite")
137
-
138
127
  self.add_control(
139
128
  "driver_logic",
140
129
  label=_("Storage type"),
@@ -143,7 +132,7 @@ class Q2AppSelect(Q2Form):
143
132
  datatype="char",
144
133
  datalen=30,
145
134
  pic=";".join(SQL_ENGINES),
146
- valid=driverLogicValid,
135
+ valid=self.driver_logic_valid,
147
136
  )
148
137
  if self.add_control("/h"):
149
138
  self.add_control(
@@ -158,7 +147,6 @@ class Q2AppSelect(Q2Form):
158
147
  _("?"),
159
148
  mess=_("Open App Storage sqlite database file"),
160
149
  control="button",
161
- datalen=3,
162
150
  valid=self.openSqliteDataFile,
163
151
  )
164
152
  self.add_control("/")
@@ -174,6 +162,19 @@ class Q2AppSelect(Q2Form):
174
162
  mess=_("Allow to change App"),
175
163
  )
176
164
  self.add_control("/")
165
+ self.add_control("/")
166
+
167
+ if self.add_control("/h", "Database Credentials"):
168
+ self.add_control("epwd", "Credentials settings", control="button", valid=self.show_password_form)
169
+ self.add_control(
170
+ "credhash",
171
+ "Credhash",
172
+ datatype="char",
173
+ datalen=256,
174
+ noform=1,
175
+ nogrid=1,
176
+ )
177
+ self.add_control("/")
177
178
 
178
179
  self.add_action(
179
180
  _("Select"),
@@ -202,6 +203,147 @@ class Q2AppSelect(Q2Form):
202
203
 
203
204
  self.actions.add_action("/crud")
204
205
 
206
+ @staticmethod
207
+ def decrypt_creds(pin, credhash):
208
+ creds = Q2Crypto(pin).decrypt(credhash)
209
+ if creds is not None:
210
+ username = creds.split(":")[0]
211
+ password = creds.split(":")[1]
212
+ return username, password
213
+ else:
214
+ return None
215
+
216
+ def show_password_form(self):
217
+ username = ""
218
+ password = ""
219
+ if self.crud_mode == "EDIT" and self.s.credhash:
220
+ pin = self.get_pin(self.r.name)
221
+ if pin is None:
222
+ return
223
+ if Q2Crypto(pin).check_pin(self.s.credhash) is None:
224
+ q2Mess("Wrong PIN")
225
+ return
226
+ creds = self.decrypt_creds(pin, self.s.credhash)
227
+ if creds is not None:
228
+ username, password = creds
229
+ else:
230
+ pin = ""
231
+
232
+ pform = Q2Form("Database credentials")
233
+ pform.add_control("/f", "Username")
234
+ pform.add_control(
235
+ "user1",
236
+ "Enter username",
237
+ datatype="char",
238
+ datalen=100,
239
+ pic="*",
240
+ data=username,
241
+ )
242
+ pform.add_control(
243
+ "user2",
244
+ "Repeat username",
245
+ datatype="char",
246
+ datalen=100,
247
+ pic="*",
248
+ data=username,
249
+ )
250
+ pform.add_control("/")
251
+
252
+ pform.add_control("/")
253
+ pform.add_control("/f", "Password")
254
+ pform.add_control(
255
+ "pass1",
256
+ "Enter password",
257
+ datatype="char",
258
+ datalen=100,
259
+ pic="*",
260
+ data=password,
261
+ )
262
+ pform.add_control(
263
+ "pass2",
264
+ "Repeat password",
265
+ datatype="char",
266
+ datalen=100,
267
+ pic="*",
268
+ data=password,
269
+ )
270
+ pform.add_control("/")
271
+ pform.add_control("/f", "Protect credentials with PIN")
272
+ pform.add_control("pin1", "Enter PIN", datatype="char", datalen=10, pic="*", data=pin)
273
+ pform.add_control("pin2", "Repeat PIN", datatype="char", datalen=10, pic="*", data=pin)
274
+ pform.add_control("/")
275
+
276
+ def after_form_show():
277
+ pass
278
+
279
+ pform.after_form_show = after_form_show
280
+
281
+ def valid():
282
+ if (pform.s.user1 or pform.s.user2) and pform.s.user1 != pform.s.user2:
283
+ q2Mess("Username mismatch")
284
+ return False
285
+ if (pform.s.pass1 or pform.s.pass2) and pform.s.pass1 != pform.s.pass2:
286
+ q2Mess("Password mismatch")
287
+ return False
288
+ if (pform.s.pin1 or pform.s.pin2) and pform.s.pin1 != pform.s.pin2:
289
+ q2Mess("PIN mismatch")
290
+ return False
291
+ return True
292
+
293
+ pform.valid = valid
294
+ pform.ok_button = 1
295
+ pform.cancel_button = 1
296
+ pform.run()
297
+
298
+ if pform.ok_pressed:
299
+ if pform.s.user1 == "" and pform.s.pass1 == "":
300
+ self.s.credhash = ""
301
+ else:
302
+ self.s.credhash = Q2Crypto(pform.s.pin1).encrypt(pform.s.user1 + ":" + pform.s.pass1)
303
+
304
+ def get_pin(self, app_name=""):
305
+ pinform = Q2Form("Enter PIN")
306
+ pinform.add_control("/")
307
+ pinform.add_control("/h")
308
+ pinform.add_control("/s")
309
+ pinform.add_control("", "Application:", control="label")
310
+ pinform.add_control("appname", app_name, control="label", style="color:green;font-weight:bold;background:white")
311
+ pinform.add_control("/s")
312
+ pinform.add_control("/")
313
+ pinform.add_control("/f")
314
+ pinform.add_control("pin", "PIN", pic="*")
315
+ pinform.ok_button = 1
316
+ pinform.cancel_button = 1
317
+
318
+ # def before_form_show():
319
+ # pinform.w.appname.set_style_sheet("color:green;font-weight:bold")
320
+
321
+ # pinform.before_form_show = before_form_show
322
+ pinform.run()
323
+ if pinform.ok_pressed:
324
+ return pinform.s.pin
325
+ else:
326
+ return None
327
+
328
+ def driver_logic_valid(self):
329
+ is_sqlite = self.s.driver_logic.lower() == "sqlite"
330
+ self.w.host_logic.set_enabled(not is_sqlite)
331
+ self.w.port_logic.set_enabled(not is_sqlite)
332
+ self.w.select_app_storage_file.set_enabled(is_sqlite)
333
+ self.credentials_fields_enable()
334
+
335
+ def driver_data_valid(self):
336
+ is_sqlite = self.s.driver_data.lower() == "sqlite"
337
+ self.w.host_data.set_enabled(not is_sqlite)
338
+ self.w.port_data.set_enabled(not is_sqlite)
339
+ self.w.select_data_storage_file.set_enabled(is_sqlite)
340
+ self.credentials_fields_enable()
341
+
342
+ def credentials_fields_enable(self):
343
+ creds_required = self.s.driver_data.lower() != "sqlite" or self.s.driver_logic.lower() != "sqlite"
344
+ # self.w.credhash.set_enabled(creds_required)
345
+ self.w.epwd.set_enabled(creds_required)
346
+
205
347
  def set_autoload(self):
206
348
  clean_this = self.r.autoselect
207
349
  self.db.cursor("update applications set autoselect='' ")
@@ -223,9 +365,6 @@ class Q2AppSelect(Q2Form):
223
365
  self.s.database_data = fname
224
366
 
225
367
  def before_grid_show(self):
226
- self.q2_app.sleep(0.2)
227
- if self.q2_app.keyboard_modifiers() != "":
228
- return
229
368
  if self.db.table("applications").row_count() <= 0:
230
369
  if not os.path.isdir("databases"):
231
370
  os.mkdir("databases")
@@ -234,15 +373,22 @@ class Q2AppSelect(Q2Form):
234
373
  {
235
374
  "ordnum": 1,
236
375
  "name": "My first app",
237
- "driver_data": "Sqlite",
376
+ "driver_data": "SQLite",
238
377
  "database_data": "databases/my_first_app_data_storage.sqlite",
239
- "driver_logic": "Sqlite",
378
+ "driver_logic": "SQLite",
240
379
  "database_logic": "databases/my_first_app_logic_storage.sqlite",
241
380
  "dev_mode": "",
242
381
  },
243
382
  self.db,
244
383
  )
245
384
  self.refresh()
385
+ elif (
386
+ q2cursor("select * from applications where autoselect<>''", self.db).row_count() == 1
387
+ ): # seek autoload
388
+ for row in range(self.model.row_count()):
389
+ if self.model.get_record(row).get("autoselect"):
390
+ self.set_grid_index(row)
391
+ break
246
392
 
247
393
  def before_crud_save(self):
248
394
  if self.s.name == "":
@@ -258,13 +404,13 @@ class Q2AppSelect(Q2Form):
258
404
  self.w.database_logic.set_focus()
259
405
  return False
260
406
 
261
- if self.s.driver_logic == "Sqlite":
407
+ if self.s.driver_logic.lower() == "sqlite":
262
408
  self.s.host_logic = ""
263
409
  self.s.port_logic = ""
264
410
  if not os.path.isdir(os.path.dirname(self.s.database_logic)):
265
411
  os.makedirs(os.path.dirname(self.s.database_logic))
266
412
 
267
- if self.s.driver_data == "Sqlite":
413
+ if self.s.driver_data.lower() == "sqlite":
268
414
  self.s.host_data = ""
269
415
  self.s.port_data = ""
270
416
  if not os.path.isdir(os.path.dirname(self.s.database_data)):
@@ -276,11 +422,19 @@ class Q2AppSelect(Q2Form):
276
422
 
277
423
  def before_form_show(self):
278
424
  if self.crud_mode == "NEW":
279
- self.s.driver_logic = "Sqlite"
280
- self.s.driver_data = "Sqlite"
425
+ self.s.driver_logic = "SQLite"
426
+ self.s.driver_data = "SQLite"
281
427
  self.s.dev_mode = ""
282
428
  self.s.database_logic = "databases/_logic"
283
429
  self.s.database_data = "databases/_data"
430
+ else:
431
+ self.s.driver_logic = SQL_ENGINES[
432
+ ["mysql", "sqlite", "postgresql"].index(self.r.driver_logic.lower())
433
+ ]
434
+ self.s.driver_data = SQL_ENGINES[
435
+ ["mysql", "sqlite", "postgresql"].index(self.r.driver_data.lower())
436
+ ]
437
+
284
438
  self.w.driver_data.valid()
285
439
  self.w.driver_logic.valid()
286
440
  if self.crud_mode in [NEW, COPY]:
@@ -289,9 +443,9 @@ class Q2AppSelect(Q2Form):
289
443
 
290
444
  def run_demo(self):
291
445
  row = {
292
- "driver_data": "Sqlite",
446
+ "driver_data": "SQLite",
293
447
  "database_data": ":memory:",
294
- "driver_logic": "Sqlite",
448
+ "driver_logic": "SQLite",
295
449
  "database_logic": ":memory:",
296
450
  "dev_mode": "",
297
451
  }
@@ -313,6 +467,19 @@ class Q2AppSelect(Q2Form):
313
467
  q2Mess(_("Can't to load Demo App"))
314
468
 
315
469
  def _select_application(self, app_data={}):
470
+ if app_data.get("credhash"):
471
+ pin = self.get_pin(app_data.get("name", ""))
472
+ if pin is None:
473
+ return False
474
+ creds = self.decrypt_creds(pin, app_data.get("credhash"))
475
+ if creds is None:
476
+ q2Mess("Wrong PIN")
477
+ return False
478
+ app_data["username"] = creds[0]
479
+ app_data["password"] = creds[1]
480
+ else:
481
+ app_data["username"] = "q2user"
482
+ app_data["password"] = "q2password"
316
483
  q2_app: Q2App = q2app.q2_app
317
484
  q2_app.dev_mode = app_data.get("dev_mode")
318
485
  q2_app.selected_application = app_data
@@ -322,11 +489,13 @@ class Q2AppSelect(Q2Form):
322
489
  q2_app.show_statusbar()
323
490
  q2_app.show_tabbar()
324
491
  self.q2_app.process_events()
492
+ return True
325
493
 
326
494
  def select_application(self):
327
- self.close()
495
+ # self.close()
328
496
  self.q2_app.process_events()
329
- self._select_application(self.model.get_record(self.current_row))
497
+ if self._select_application(self.model.get_record(self.current_row)):
498
+ self.close()
330
499
 
331
500
  def run(self, autoload_enabled=True):
332
501
  q2_app: Q2App = q2app.q2_app
@@ -338,9 +507,12 @@ class Q2AppSelect(Q2Form):
338
507
  q2_app.hide_tabbar()
339
508
 
340
509
  self.autoload_enabled = autoload_enabled
341
- if autoload_enabled:
510
+ self.q2_app.process_events()
511
+ self.q2_app.sleep(0.1)
512
+ km = self.q2_app.keyboard_modifiers()
513
+ if autoload_enabled and km != "shift":
342
514
  cu = q2cursor("select * from applications where autoselect<>''", self.db)
343
515
  if cu.row_count() > 0:
344
- self._select_application(cu.record(0))
345
- return False
516
+ if self._select_application(cu.record(0)):
517
+ return False
346
518
  super().run()
@@ -188,6 +188,7 @@ class Q2Lines(Q2Form, Q2_save_and_run):
188
188
  _("?"),
189
189
  mess=_("Open list of existing tables"),
190
190
  control="button",
191
+ datalen=2,
191
192
  valid=self.select_linked_table,
192
193
  )
193
194
  self.add_control("to_table", gridlabel=_("To table"), datatype="char", datalen=100)
@@ -198,6 +199,7 @@ class Q2Lines(Q2Form, Q2_save_and_run):
198
199
  _("?"),
199
200
  mess=_("Open list of existing tables"),
200
201
  control="button",
202
+ datalen=2,
201
203
  valid=self.select_linked_table_pk,
202
204
  )
203
205
  self.add_control("to_column", gridlabel=_("To field"), datatype="char", datalen=100)
@@ -209,6 +211,7 @@ class Q2Lines(Q2Form, Q2_save_and_run):
209
211
  _("?"),
210
212
  mess=_("Open list of existing columns"),
211
213
  control="button",
214
+ datalen=2,
212
215
  valid=self.select_linked_table_column,
213
216
  )
214
217
  self.add_control("/")
@@ -222,6 +225,7 @@ class Q2Lines(Q2Form, Q2_save_and_run):
222
225
  _("?"),
223
226
  mess=_("Open list of existing forms"),
224
227
  control="button",
228
+ datalen=2,
225
229
  valid=self.select_linked_form,
226
230
  )
227
231
  self.add_control("to_form", gridlabel=_("Form to open"), datatype="char", datalen=100)
@@ -271,6 +275,13 @@ class Q2Lines(Q2Form, Q2_save_and_run):
271
275
  nogrid="*",
272
276
  datatype="longtext",
273
277
  )
278
+ self.add_control("/t", _("Stylesheet"))
279
+ self.add_control(
280
+ "style",
281
+ control="code",
282
+ nogrid="*",
283
+ datatype="longtext",
284
+ )
274
285
  self.add_control("/")
275
286
  self._add_save_and_run()
276
287
  self._add_save_and_run_visible()
@@ -321,11 +332,11 @@ class Q2Lines(Q2Form, Q2_save_and_run):
321
332
 
322
333
  if last < self.model.row_count():
323
334
  last_seq = int_(self.model.get_record(last)["seq"]) + 1
324
- self.move_rows_down(last, False)
335
+ self.move_rows_down(last)
325
336
  else:
326
337
  last_seq = int_(self.model.get_record(self.model.row_count() - 1)["seq"]) + 2
327
338
 
328
- self.move_rows_down(first, False)
339
+ self.move_rows_down(first)
329
340
  self.model.insert({"column": layout_type, "seq": first_seq})
330
341
  self.model.insert({"column": "/", "seq": last_seq})
331
342
  self.refresh()
@@ -335,7 +346,7 @@ class Q2Lines(Q2Form, Q2_save_and_run):
335
346
  current_row = self.current_row
336
347
  for x in range(current_row, self.model.row_count()):
337
348
  rec = self.model.get_record(x)
338
- self.model.update({"id": rec["id"], "seq": 1 + int_(rec["seq"])})
349
+ self.model.update({"id": rec["id"], "seq": 1 + int_(rec["seq"])}, refresh=False)
339
350
  if refresh:
340
351
  self.refresh()
341
352
 
@@ -350,17 +361,22 @@ class Q2Lines(Q2Form, Q2_save_and_run):
350
361
 
351
362
  def seek_end():
352
363
  nonlocal last_row
364
+ same_panel = 0
353
365
  while last_row < self.model.row_count():
354
366
  self.set_grid_index(last_row)
355
- # if is_panel_start():
356
- # break
357
- if is_panel_end():
358
- break
367
+ if is_panel_start():
368
+ same_panel += 1
369
+ elif is_panel_end():
370
+ if same_panel == 1:
371
+ break
372
+ else:
373
+ same_panel -= 1
359
374
  last_row += 1
360
375
 
361
376
  def seek_start():
362
- in_panel = -1 if is_panel_end() else 0
363
377
  nonlocal first_row
378
+ self.set_grid_index(first_row)
379
+ in_panel = -1 if is_panel_end() else 0
364
380
  while first_row > 0:
365
381
  self.set_grid_index(first_row)
366
382
  if is_panel_start():
@@ -374,6 +390,7 @@ class Q2Lines(Q2Form, Q2_save_and_run):
374
390
 
375
391
  if not is_panel_start():
376
392
  seek_start()
393
+ last_row = first_row
377
394
  if not is_panel_end():
378
395
  seek_end()
379
396
  self.set_grid_selected_rows([x for x in range(first_row, last_row + 1)])
@@ -297,9 +297,9 @@ class Q2RadApp(Q2App):
297
297
  self.open_selected_app(True)
298
298
  self.check_app_update()
299
299
  self.on_new_tab()
300
- # self.update_app_packages()
301
300
  else:
302
301
  self.close()
302
+ self.subwindow_count_changed()
303
303
 
304
304
  def open_selected_app(self, go_to_q2market=False):
305
305
  wait = Q2WaitShow(5, "Loading app> ")
@@ -475,8 +475,8 @@ class Q2RadApp(Q2App):
475
475
  host=self.selected_application.get("host_data", ""),
476
476
  port=self.selected_application.get("port_data", ""),
477
477
  guest_mode=self.selected_application.get("guest_mode", ""),
478
- user="q2user",
479
- password="q2password",
478
+ user=self.selected_application.get("username", ""),
479
+ password=self.selected_application.get("password", ""),
480
480
  )
481
481
  self.db = self.db_data
482
482
  if self.db_data:
@@ -485,8 +485,8 @@ class Q2RadApp(Q2App):
485
485
  db_engine_name=self.selected_application.get("driver_logic", "").lower(),
486
486
  host=self.selected_application.get("host_logic", ""),
487
487
  port=self.selected_application.get("port_logic", ""),
488
- user="q2user",
489
- password="q2password",
488
+ user=self.selected_application.get("username", ""),
489
+ password=self.selected_application.get("password", ""),
490
490
  )
491
491
  self.last_root_password = ""
492
492
  if self.db_data is None or self.db_logic is None:
@@ -1040,6 +1040,7 @@ class Q2RadApp(Q2App):
1040
1040
  , code_valid as valid
1041
1041
  , code_when as _when
1042
1042
  , code_show as _show
1043
+ , style
1043
1044
  from `lines`
1044
1045
  where name = '{name}'
1045
1046
  order by seq
@@ -81,6 +81,7 @@ class Q2Form(_Q2Form):
81
81
  tag="",
82
82
  eat_enter=None,
83
83
  hotkey="",
84
+ style="",
84
85
  **args,
85
86
  ):
86
87
  if isinstance(to_form, str) and to_form != "":
@@ -123,6 +124,7 @@ class Q2Form(_Q2Form):
123
124
  tag,
124
125
  eat_enter,
125
126
  hotkey,
127
+ style,
126
128
  **args,
127
129
  )
128
130
 
@@ -490,13 +492,14 @@ class Q2_save_and_run:
490
492
 
491
493
 
492
494
  class auto_filter:
493
- def __init__(self, form_name, mem):
495
+ def __init__(self, form_name, mem, lines_per_tab=10):
494
496
  self.form_name = form_name
495
497
  self.mem = mem
496
498
  self.filter_columns = []
497
499
  self.mem.ok_button = True
498
500
  self.mem.cancel_button = True
499
501
  self.mem.add_ok_cancel_buttons()
502
+ self.lines_per_tab = lines_per_tab
500
503
 
501
504
  self.auto_filter()
502
505
 
@@ -513,8 +516,13 @@ class auto_filter:
513
516
  """,
514
517
  self.mem.q2_app.db_logic,
515
518
  )
516
- self.mem.add_control("/f")
519
+ make_tabs = cu.row_count() > self.lines_per_tab
520
+ if not make_tabs:
521
+ self.mem.add_control("/f")
517
522
  for col in cu.records():
523
+ if make_tabs and cu.current_row() % self.lines_per_tab == 0:
524
+ self.mem.add_control("/t", f"Tab #{1+ cu.current_row() // self.lines_per_tab}")
525
+ self.mem.add_control("/f")
518
526
  if col["control"] == "text":
519
527
  col["control"] = "line"
520
528
  col = Q2Controls.validate(col)
@@ -536,6 +544,7 @@ class auto_filter:
536
544
  col["check"] = 1
537
545
 
538
546
  self.mem.add_control(**col)
547
+ self.mem.add_control("/")
539
548
  self.mem.valid = self.valid
540
549
 
541
550
  def valid(self):
@@ -0,0 +1 @@
1
+ __version__ = "0.1.204"
@@ -1 +0,0 @@
1
- __version__ = "0.1.202"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes