mapdata 2.12.0__tar.gz → 2.14.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapdata
3
- Version: 2.12.0
3
+ Version: 2.14.0
4
4
  Summary: An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database
5
5
  Home-page: https://osdn.net/project/mapdata/
6
6
  Author: Dreas Nielsen
@@ -24,8 +24,8 @@
24
24
  #
25
25
  # ==================================================================
26
26
 
27
- version = "2.12.0"
28
- vdate = "2023-12-28"
27
+ version = "2.14.0"
28
+ vdate = "2023-12-31"
29
29
 
30
30
  copyright = "2023"
31
31
 
@@ -459,15 +459,23 @@ class CsvFile(object):
459
459
  return self
460
460
 
461
461
 
462
- def sort_columns(columns):
463
- # Sorts a list of one or two sublists, where each sublist is a column. Rows are sorted by the first column.
462
+ def sort_columns(columns, sortby=0):
463
+ # Sorts a list of one, two, or three sublists, where each sublist is a column.
464
+ # Rows are sorted by the 'sortby' column, which is zero-based.
464
465
  # The returned value is also column-wise, but sorted by rows.
465
466
  if len(columns) == 1:
466
467
  return sorted(columns)
467
468
  nrows = len(columns[0])
468
- rowdata = [[columns[0][i], columns[1][i]] for i in range(nrows)]
469
- rowdata.sort()
470
- return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)]]
469
+ if len(columns) == 2:
470
+ rowdata = [[columns[0][i], columns[1][i]] for i in range(nrows)]
471
+ rowdata.sort(key = lambda c: c[sortby])
472
+ return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)]]
473
+ else:
474
+ # len(columns) should be 3, though this is not checked
475
+ rowdata = [[columns[0][i], columns[1][i], columns[2][i]] for i in range(nrows)]
476
+ rowdata.sort(key = lambda c: c[sortby])
477
+ return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)], [rowdata[i][2] for i in range(nrows)]]
478
+
471
479
 
472
480
  def treeview_sort_column(tv, col, reverse):
473
481
  # Sort columns in Tkinter Treeview. From https://stackoverflow.com/questions/1966929/tk-treeview-column-sort#1967793
@@ -906,6 +914,7 @@ class MapUI(object):
906
914
  self.basemap_var = tk.StringVar(self.win, bm_name)
907
915
  self.map_option_menu = ttk.Combobox(ctrlframe, state="readonly", textvariable=self.basemap_var,
908
916
  values=self.available_tile_servers(), width=18)
917
+ self.map_option_menu["state"] = "normal"
909
918
  self.map_option_menu.bind('<<ComboboxSelected>>', self.change_basemap)
910
919
  self.map_option_menu.grid(row=0, column=1, padx=(5, 30), pady=(2, 5), sticky=tk.W)
911
920
  # Multi-select option
@@ -1409,8 +1418,9 @@ class MapUI(object):
1409
1418
  # Plotting support. Return all data for the specified columns as a list of column-oriented lists.
1410
1419
  res = []
1411
1420
  for c in column_list:
1412
- i = self.headers.index(c)
1413
- res.append([row[i] for row in self.rows])
1421
+ if c in self.headers:
1422
+ i = self.headers.index(c)
1423
+ res.append([row[i] for row in self.rows])
1414
1424
  return res
1415
1425
  def get_sel_data(self, column_list):
1416
1426
  # Plotting support. Return data from selected rows for the specified columns, as a list of lists.
@@ -1445,8 +1455,10 @@ class MapUI(object):
1445
1455
  clone.ylog_var.set(plot_obj.ylog_var.get())
1446
1456
  clone.x_sel["values"] = copy.copy(plot_obj.x_sel["values"])
1447
1457
  clone.y_sel["values"] = copy.copy(plot_obj.y_sel["values"])
1458
+ clone.groupby_sel["values"] = copy.copy(plot_obj.groupby_sel["values"])
1448
1459
  clone.x_sel["state"] = plot_obj.x_sel["state"]
1449
1460
  clone.y_sel["state"] = plot_obj.y_sel["state"]
1461
+ clone.groupby_sel["state"] = plot_obj.groupby_sel["state"]
1450
1462
  clone.xlog_ck["state"] = plot_obj.xlog_ck["state"]
1451
1463
  clone.ylog_ck["state"] = plot_obj.ylog_ck["state"]
1452
1464
  clone.data_btn["state"] = plot_obj.data_btn["state"]
@@ -1460,6 +1472,9 @@ class MapUI(object):
1460
1472
  clone.dlg.bind("<Alt-y>", clone.set_ylabel)
1461
1473
  if clone.type_var.get() == "Histogram":
1462
1474
  clone.dlg.bind("<Alt-b>", clone.set_bins)
1475
+ clone.alpha = plot_obj.alpha
1476
+ if clone.type_var.get() in ("Scatter plot", "Categorical stripchart", "Categorical KD plot"):
1477
+ clone.dlg.bind("<Alt-a>", clone.set_alpha)
1463
1478
  clone.dataset = copy.copy(plot_obj.dataset)
1464
1479
  clone.plot_data = copy.copy(plot_obj.plot_data)
1465
1480
  clone.data_labels = copy.copy(plot_obj.data_labels)
@@ -2786,10 +2801,16 @@ class PlotDialog(object):
2786
2801
  self.dlg.columnconfigure(0, weight=1)
2787
2802
  self.auto_update = True
2788
2803
  self.plot_title = None
2804
+ # For transparency on some plots
2805
+ self.alpha = 0.45
2789
2806
  # For histogram
2790
2807
  self.bins = 10
2791
- # For groups at Jenks breaks on Q-Q plots
2808
+ # For display of groups at Jenks breaks on Q-Q plots
2792
2809
  self.qq_groups = False
2810
+ # For display of lines at X and Y Jenks breaks on scatter plots
2811
+ self.scatter_breaks = False
2812
+ self.scatter_x_breaks = None
2813
+ self.scatter_y_breaks = None
2793
2814
 
2794
2815
  def set_autoupdate():
2795
2816
  if self.autoupdate_var.get() == "1":
@@ -2818,6 +2839,7 @@ class PlotDialog(object):
2818
2839
  self.type_sel = ttk.Combobox(ctrl_frame, state="readonly", textvariable=self.type_var, width=20, height=15,
2819
2840
  values=["Box plot", "Breaks groups", "Breaks optimum", "Category counts", "Categorical KD plot", "Categorical stripchart", "Empirical CDF", "Histogram", "Kernel density (KD) plot", "Line plot", "Min-max plot", "Normal Q-Q plot", "Scatter plot", "Violin plot", "Y range plot"])
2820
2841
  self.type_sel.grid(row=0, column=1, columnspan=2, sticky=tk.W, padx=(3,6), pady=(3,3))
2842
+ self.type_sel["state"] = "normal"
2821
2843
  self.type_sel.bind("<<ComboboxSelected>>", self.set_xy)
2822
2844
 
2823
2845
  self.sel_only_var = tk.StringVar(ctrl_frame, "0")
@@ -2833,14 +2855,14 @@ class PlotDialog(object):
2833
2855
  self.x_var = tk.StringVar(ctrl_frame, "")
2834
2856
  x_lbl = ttk.Label(ctrl_frame, text="X column:")
2835
2857
  x_lbl.grid(row=0, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2836
- self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=20)
2858
+ self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=24)
2837
2859
  self.x_sel.grid(row=0, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2838
2860
  self.x_sel.bind("<<ComboboxSelected>>", self.x_changed)
2839
2861
 
2840
2862
  self.y_var = tk.StringVar(ctrl_frame, "")
2841
2863
  y_lbl = ttk.Label(ctrl_frame, text="Y column:")
2842
2864
  y_lbl.grid(row=1, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2843
- self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=20)
2865
+ self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=24)
2844
2866
  self.y_sel.grid(row=1, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2845
2867
  self.y_sel.bind("<<ComboboxSelected>>", self.y_changed)
2846
2868
 
@@ -2854,6 +2876,13 @@ class PlotDialog(object):
2854
2876
  onvalue="1", offvalue="0")
2855
2877
  self.ylog_ck.grid(row=1, column=5, sticky=tk.W, padx=(6,6), pady=(3,3))
2856
2878
 
2879
+ self.groupby_var = tk.StringVar(ctrl_frame, "")
2880
+ groupby_lbl = ttk.Label(ctrl_frame, text="Group by:")
2881
+ groupby_lbl.grid(row=2, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2882
+ self.groupby_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.groupby_var, width=24)
2883
+ self.groupby_sel.grid(row=2, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2884
+ self.groupby_sel.bind("<<ComboboxSelected>>", self.groupby_changed)
2885
+
2857
2886
  # Plot
2858
2887
  self.content_frame = tk.Frame(self.dlg, borderwidth=3, relief=tk.RIDGE)
2859
2888
  self.content_frame.grid(row=2, column=0, sticky=tk.NSEW)
@@ -2913,7 +2942,7 @@ class PlotDialog(object):
2913
2942
  for j in range(variables):
2914
2943
  row.append(self.dataset[j][i])
2915
2944
  rowwise_data.append(row)
2916
- tframe, tdata = treeview_table(dlg.content_frame, rowwise_data, self.data_labels)
2945
+ tframe, tdata = treeview_table(dlg.content_frame, rowwise_data, self.data_labels[0:variables])
2917
2946
  tframe.grid(row=0, column=0, sticky=tk.NSEW)
2918
2947
  dlg.show()
2919
2948
 
@@ -2956,10 +2985,15 @@ class PlotDialog(object):
2956
2985
  self.ylog_ck["state"] = "normal"
2957
2986
  self.q_redraw(args)
2958
2987
 
2988
+ def groupby_changed(self, *args):
2989
+ self.q_redraw(args)
2990
+
2959
2991
  def set_xy(self, *args):
2960
2992
  # Enable X and Y value selection, and set Combobox values based on plot type and column types.
2993
+ # Also sets the groupby Combobox values if applicable.
2961
2994
  self.plotfig.clear()
2962
2995
  self.plot_title = None
2996
+ self.dlg.bind("<Alt-a>")
2963
2997
  self.dlg.bind("<Alt-b>")
2964
2998
  self.dlg.bind("<Alt-g>")
2965
2999
  self.plot_axes = self.plotfig.add_subplot(111)
@@ -2978,24 +3012,28 @@ class PlotDialog(object):
2978
3012
  # quant_columns includes date and timestamp columns
2979
3013
  quant_columns = [c[0] for c in self.column_specs if c[1] in ("int", "float", "date", "timestamp", "timestamptz")]
2980
3014
  quant_columns.sort()
2981
- numeric_columns = [c[0] for c in self.column_specs if c[1] in ("int", "float")]
2982
- numeric_columns.sort()
3015
+ self.numeric_columns = [c[0] for c in self.column_specs if c[1] in ("int", "float")]
3016
+ self.numeric_columns.sort()
2983
3017
  date_columns = [c[0] for c in self.column_specs if c[1] in ("date", "timestamp", "timestamptz")]
2984
3018
  date_columns.sort()
2985
3019
  self.x_var.set('')
2986
3020
  self.y_var.set('')
3021
+ self.groupby_var.set('')
2987
3022
  self.xlog_ck["state"] = "normal"
2988
3023
  self.ylog_ck["state"] = "normal"
2989
3024
  self.x_sel["state"] = "readonly"
2990
3025
  self.y_sel["state"] = "readonly"
3026
+ self.groupby_sel["state"] = "readonly"
2991
3027
  plot_type = self.type_var.get()
2992
3028
  if plot_type == "Category counts":
3029
+ self.x_sel["state"] = "normal"
2993
3030
  self.x_sel["values"] = categ_columns
2994
3031
  self.xlog_ck["state"] = "disabled"
2995
3032
  self.y_sel["state"] = "disabled"
2996
3033
  self.ylog_ck["state"] = "disabled"
2997
3034
  elif plot_type in ("Histogram", "Empirical CDF", "Kernel density (KD) plot", "Normal Q-Q plot", "Breaks groups", "Breaks optimum"):
2998
- self.x_sel["values"] = numeric_columns
3035
+ self.x_sel["state"] = "normal"
3036
+ self.x_sel["values"] = self.numeric_columns
2999
3037
  self.y_sel["state"] = "disabled"
3000
3038
  self.ylog_ck["state"] = "disabled"
3001
3039
  if plot_type == "Histogram":
@@ -3005,16 +3043,25 @@ class PlotDialog(object):
3005
3043
  elif plot_type in ("Box plot", "Categorical KD plot", "Categorical stripchart", "Violin plot"):
3006
3044
  xcols = list(set(categ_columns) | set(date_columns))
3007
3045
  xcols.sort()
3046
+ self.x_sel["state"] = "normal"
3008
3047
  self.x_sel["values"] = xcols
3009
3048
  self.xlog_ck["state"] = "disabled"
3010
- self.y_sel["values"] = numeric_columns
3049
+ self.y_sel["state"] = "normal"
3050
+ self.y_sel["values"] = self.numeric_columns
3011
3051
  elif plot_type == "Min-max plot":
3052
+ self.x_sel["state"] = "normal"
3012
3053
  self.x_sel["values"] = quant_columns
3054
+ self.y_sel["state"] = "normal"
3013
3055
  self.y_sel["values"] = categ_columns2
3014
3056
  self.ylog_ck["state"] = "disabled"
3015
3057
  else:
3058
+ self.x_sel["state"] = "normal"
3016
3059
  self.x_sel["values"] = quant_columns
3060
+ self.y_sel["state"] = "normal"
3017
3061
  self.y_sel["values"] = quant_columns
3062
+ if plot_type in ("Scatter plot", "Line plot"):
3063
+ self.groupby_sel["state"] = "normal"
3064
+ self.groupby_sel["values"] = [''] + categ_columns2 + ['* Breaks in X', '* Breaks in Y']
3018
3065
 
3019
3066
  def q_redraw(self, get_data=True, *args):
3020
3067
  # Conditionally (re)draw the plot.
@@ -3041,6 +3088,8 @@ class PlotDialog(object):
3041
3088
  column_list = [self.x_var.get()]
3042
3089
  if self.y_var.get() != '':
3043
3090
  column_list.append(self.y_var.get())
3091
+ if self.groupby_var.get() != '':
3092
+ column_list.append(self.groupby_var.get())
3044
3093
  # Get either only the selected data or all data.
3045
3094
  if self.sel_only_var.get() == "1":
3046
3095
  dataset = self.parent.get_sel_data(column_list)
@@ -3082,6 +3131,9 @@ class PlotDialog(object):
3082
3131
  # Set data labels
3083
3132
  if self.y_var.get() != '':
3084
3133
  self.data_labels = [self.x_var.get(), self.y_var.get()]
3134
+ grpvar = self.groupby_var.get()
3135
+ if grpvar not in ('', '* Breaks in X', '* Breaks in Y') :
3136
+ self.data_labels.append(grpvar)
3085
3137
  else:
3086
3138
  self.data_labels = [self.x_var.get()]
3087
3139
  # Log-transform data if specified.
@@ -3158,7 +3210,7 @@ class PlotDialog(object):
3158
3210
  self.plot_data_labels = [self.data_labels[0], "Cumulative frequency"]
3159
3211
  elif plot_type == "Kernel density (KD) plot":
3160
3212
  self.plot_data = self.dataset
3161
- #self.plot_data_labels = [self.x_var.get()]
3213
+ self.plot_data_labels = [self.x_var.get()]
3162
3214
  elif plot_type == "Min-max plot":
3163
3215
  # Min and max X for each Y
3164
3216
  y_vals = list(set(self.dataset[1]))
@@ -3209,12 +3261,49 @@ class PlotDialog(object):
3209
3261
  self.plot_data_labels = [self.data_labels[0], self.data_labels[1] + " min", self.data_labels[1] + " max"]
3210
3262
  elif plot_type == "Line plot":
3211
3263
  # Sort by X
3212
- ds = list(zip(self.dataset[0], self.dataset[1]))
3213
- ds.sort()
3214
- ds2 = list(zip(*ds))
3215
- self.plot_data = [list(ds2[0]), list(ds2[1])]
3264
+ #if self.groupby_var.get() == '':
3265
+ # ds = list(zip(self.dataset[0], self.dataset[1]))
3266
+ #else:
3267
+ # ds = list(zip(self.dataset[0], self.dataset[1], self.dataset[2]))
3268
+ #ds.sort()
3269
+ #ds2 = list(zip(*ds))
3270
+ ds2 = sort_columns(self.dataset)
3271
+ if self.groupby_var.get() == '':
3272
+ self.plot_data = [list(ds2[0]), list(ds2[1])]
3273
+ else:
3274
+ self.plot_data = [list(ds2[0]), list(ds2[1]), list(ds2[2])]
3216
3275
  self.plot_data_labels = self.data_labels
3217
- elif plot_type in ("Histogram", "Scatter plot", "Y range plot"):
3276
+ elif plot_type == "Scatter plot":
3277
+ if self.groupby_var.get() == "* Breaks in X":
3278
+ if self.x_var.get() in self.numeric_columns:
3279
+ ds = sort_columns(self.dataset)
3280
+ oj = optimum_jenks(ds[0], 8)
3281
+ jnb = jenkspy.JenksNaturalBreaks(oj)
3282
+ jnb.fit(ds[0])
3283
+ self.plot_data = [ds[0], ds[1], ["Group "+str(lbl+1) for lbl in jnb.labels_]]
3284
+ self.plot_data_labels = self.data_labels + ['Breaks in X']
3285
+ else:
3286
+ # Can't compute breaks for X
3287
+ self.groupby_var.set('')
3288
+ self.plot_data = self.dataset
3289
+ self.plot_data_labels = self.data_labels
3290
+ elif self.groupby_var.get() == "* Breaks in Y":
3291
+ if self.y_var.get() in self.numeric_columns:
3292
+ ds = sort_columns(self.dataset, sortby=1)
3293
+ oj = optimum_jenks(ds[1], 8)
3294
+ jnb = jenkspy.JenksNaturalBreaks(oj)
3295
+ jnb.fit(ds[1])
3296
+ self.plot_data = [ds[0], ds[1], ["Group "+str(lbl+1) for lbl in jnb.labels_]]
3297
+ self.plot_data_labels = self.data_labels + ['Breaks in Y']
3298
+ else:
3299
+ # Can't compute breaks for Y
3300
+ self.groupby_var.set('')
3301
+ self.plot_data = self.dataset
3302
+ self.plot_data_labels = self.data_labels
3303
+ else:
3304
+ self.plot_data = self.dataset
3305
+ self.plot_data_labels = self.data_labels
3306
+ elif plot_type in ("Histogram", "Y range plot"):
3218
3307
  # No special preparation
3219
3308
  self.plot_data = self.dataset
3220
3309
  self.plot_data_labels = self.data_labels
@@ -3232,11 +3321,52 @@ class PlotDialog(object):
3232
3321
  self.plot_axes.set_xlabel(self.x_var.get())
3233
3322
  self.plot_axes.set_ylabel("Counts")
3234
3323
  elif plot_type == "Scatter plot":
3235
- self.plot_axes.scatter(self.plot_data[0], self.plot_data[1])
3324
+ self.dlg.bind("<Alt-a>", self.set_alpha)
3325
+ self.dlg.bind("<Alt-b>", self.set_scatter_breaks)
3326
+ if self.scatter_breaks:
3327
+ if self.x_var.get() in self.numeric_columns:
3328
+ x_vals = copy.copy(self.plot_data[0])
3329
+ x_vals.sort()
3330
+ oj = optimum_jenks(x_vals, 8)
3331
+ jnb = jenkspy.JenksNaturalBreaks(oj)
3332
+ jnb.fit(x_vals)
3333
+ self.scatter_x_breaks = jnb.inner_breaks_
3334
+ for b in self.scatter_x_breaks:
3335
+ self.plot_axes.axvline(b, color='0.8', alpha=0.5)
3336
+ if self.y_var.get() in self.numeric_columns:
3337
+ y_vals = copy.copy(self.plot_data[1])
3338
+ y_vals.sort()
3339
+ oj = optimum_jenks(y_vals, 8)
3340
+ jnb = jenkspy.JenksNaturalBreaks(oj)
3341
+ jnb.fit(y_vals)
3342
+ self.scatter_y_breaks = jnb.inner_breaks_
3343
+ for b in self.scatter_y_breaks:
3344
+ self.plot_axes.axhline(b, color='0.8', alpha=0.5)
3345
+ if self.groupby_var.get() == '':
3346
+ self.plot_axes.scatter(self.plot_data[0], self.plot_data[1], alpha=self.alpha)
3347
+ else:
3348
+ groups = list(set(self.plot_data[2]))
3349
+ groups.sort()
3350
+ datarows = len(self.plot_data[0])
3351
+ for g in groups:
3352
+ pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
3353
+ pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
3354
+ self.plot_axes.plot(pdx, pdy, marker='o', alpha=self.alpha, linestyle='', label=g)
3355
+ self.plot_axes.legend()
3236
3356
  self.plot_axes.set_xlabel(self.plot_data_labels[0])
3237
3357
  self.plot_axes.set_ylabel(self.plot_data_labels[1])
3238
3358
  elif plot_type == "Line plot":
3239
- self.plot_axes.plot(self.plot_data[0], self.plot_data[1])
3359
+ if self.groupby_var.get() == '':
3360
+ self.plot_axes.plot(self.plot_data[0], self.plot_data[1])
3361
+ else:
3362
+ groups = list(set(self.plot_data[2]))
3363
+ groups.sort()
3364
+ datarows = len(self.plot_data[0])
3365
+ for g in groups:
3366
+ pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
3367
+ pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
3368
+ self.plot_axes.plot(pdx, pdy, label=g)
3369
+ self.plot_axes.legend()
3240
3370
  self.plot_axes.set_xlabel(self.plot_data_labels[0])
3241
3371
  self.plot_axes.set_ylabel(self.plot_data_labels[1])
3242
3372
  elif plot_type == "Breaks groups":
@@ -3299,14 +3429,16 @@ class PlotDialog(object):
3299
3429
  self.plot_axes.set_xlabel(self.x_var.get())
3300
3430
  self.plot_axes.set_ylabel(self.data_labels[1])
3301
3431
  elif plot_type == "Categorical KD plot":
3432
+ self.dlg.bind("<Alt-a>", self.set_alpha)
3302
3433
  sns.kdeplot({self.x_var.get(): self.dataset[0], self.y_var.get(): self.dataset[1]},
3303
- x=self.y_var.get(), hue=self.x_var.get(), multiple="layer", fill=True, alpha=0.35, ax=self.plot_axes,
3434
+ x=self.y_var.get(), hue=self.x_var.get(), multiple="layer", fill=True, alpha=self.alpha, ax=self.plot_axes,
3304
3435
  warn_singular=False)
3305
3436
  self.plot_axes.set_xlabel(self.y_var.get())
3306
3437
  self.plot_axes.set_ylabel("Density")
3307
3438
  elif plot_type == "Categorical stripchart":
3439
+ self.dlg.bind("<Alt-a>", self.set_alpha)
3308
3440
  sns.stripplot({self.x_var.get(): self.dataset[0], self.y_var.get(): self.dataset[1]},
3309
- x=self.x_var.get(), y=self.y_var.get(), alpha=0.4, ax=self.plot_axes)
3441
+ x=self.x_var.get(), y=self.y_var.get(), alpha=self.alpha, ax=self.plot_axes)
3310
3442
  self.plot_axes.set_xlabel(self.x_var.get())
3311
3443
  self.plot_axes.set_ylabel(self.data_labels[1])
3312
3444
  elif plot_type == "Violin plot":
@@ -3345,6 +3477,20 @@ class PlotDialog(object):
3345
3477
  self.bins = num_bins
3346
3478
  if self.type_var.get() == "Histogram":
3347
3479
  self.q_redraw()
3480
+ def set_alpha(self, *args):
3481
+ dlg = OneFloatDialog(self.dlg, "Transparency", "Enter the transparency (alpha) value", min_value=0.0, max_value=1.0, initial=self.alpha)
3482
+ new_alpha = dlg.show()
3483
+ if new_alpha is not None:
3484
+ self.alpha = new_alpha
3485
+ if self.type_var.get() in ("Scatter plot", "Categorical stripchart", "Categorical KD plot"):
3486
+ self.q_redraw()
3487
+ def set_scatter_breaks(self, *args):
3488
+ # Toggle display of vertical and horizontal lines at natural breaks values on a scatter plot.
3489
+ # plot_data[0] and plot_data[1] must be x and y, respectively.
3490
+ self.scatter_breaks = not self.scatter_breaks
3491
+ if self.type_var.get() == "Scatter plot":
3492
+ self.q_redraw()
3493
+
3348
3494
  def show_groups(self, *args):
3349
3495
  if self.type_var.get() == "Normal Q-Q plot":
3350
3496
  self.qq_groups = not self.qq_groups
@@ -3562,6 +3708,53 @@ class OneIntDialog(object):
3562
3708
  return self.entry_var.get()
3563
3709
 
3564
3710
 
3711
+ class OneFloatDialog(object):
3712
+ def __init__(self, parent, title, prompt, min_value, max_value, initial):
3713
+ self.dlg = tk.Toplevel(parent)
3714
+ self.dlg.title(title)
3715
+ prompt_frame = tk.Frame(self.dlg)
3716
+ prompt_frame.grid(row=0, column=0, sticky=tk.NSEW, pady=(3,3))
3717
+ prompt_frame.columnconfigure(0, weight=1)
3718
+ msg_lbl = ttk.Label(prompt_frame, text=prompt)
3719
+ msg_lbl.grid(row=0, column=0, sticky=tk.W, padx=(6,6), pady=(6,6))
3720
+ self.entry_var = tk.DoubleVar(self.dlg, initial)
3721
+ self.val_entry = ttk.Spinbox(prompt_frame, textvariable=self.entry_var, from_=min_value, to=max_value)
3722
+ self.val_entry.grid(row=1, column=0, sticky=tk.EW, padx=(6,6), pady=(3,3))
3723
+ btn_frame = tk.Frame(self.dlg, borderwidth=3, relief=tk.RIDGE)
3724
+ btn_frame.columnconfigure(0, weight=1)
3725
+ btn_frame.grid(row=2, column=0, sticky=tk.EW, pady=(3,3))
3726
+ btn_frame.columnconfigure(0, weight=1)
3727
+ self.val_entry.focus()
3728
+ # Buttons
3729
+ self.canceled = False
3730
+ self.ok_btn = ttk.Button(btn_frame, text="OK", command=self.do_select, underline=0)
3731
+ self.ok_btn.grid(row=0, column=0, sticky=tk.E, padx=(6,3))
3732
+ self.dlg.bind('<Alt-o>', self.do_select)
3733
+ cancel_btn = ttk.Button(btn_frame, text="Cancel", command=self.do_cancel, underline=0)
3734
+ cancel_btn.grid(row=0, column=1, sticky=tk.E, padx=(3,6))
3735
+ self.dlg.bind("<Escape>", self.do_cancel)
3736
+ self.dlg.bind("<Alt-c>", self.do_cancel)
3737
+ self.dlg.bind("<Return>", self.do_select)
3738
+ self.dlg.bind("<Alt-o>", self.do_select)
3739
+ def do_cancel(self, *args):
3740
+ self.canceled = True
3741
+ self.dlg.destroy()
3742
+ def do_select(self, *args):
3743
+ self.canceled = False
3744
+ self.dlg.destroy()
3745
+ def show(self):
3746
+ self.dlg.grab_set()
3747
+ center_window(self.dlg)
3748
+ raise_window(self.dlg)
3749
+ self.dlg.resizable(True, False)
3750
+ self.dlg.attributes('-topmost', 'true')
3751
+ self.dlg.wait_window(self.dlg)
3752
+ if self.canceled:
3753
+ return None
3754
+ else:
3755
+ return self.entry_var.get()
3756
+
3757
+
3565
3758
  class GetEditorDialog(object):
3566
3759
  def __init__(self, parent, current_editor):
3567
3760
  self.dlg = tk.Toplevel(parent)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapdata
3
- Version: 2.12.0
3
+ Version: 2.14.0
4
4
  Summary: An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database
5
5
  Home-page: https://osdn.net/project/mapdata/
6
6
  Author: Dreas Nielsen
@@ -5,7 +5,7 @@ with io.open('README.md', encoding='utf-8') as f:
5
5
  long_description = f.read()
6
6
 
7
7
  setuptools.setup(name='mapdata',
8
- version='2.12.0',
8
+ version='2.14.0',
9
9
  description="An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database",
10
10
  author='Dreas Nielsen',
11
11
  author_email='dreas.nielsen@gmail.com',
File without changes
File without changes
File without changes
File without changes
File without changes