Need to set default value of widget based on another widget in databricks - widget

metro_choices = [str(metro_df.select('METRO').distinct().collect()[i][0])
for i in range(len(metro_df.select('METRO').distinct().collect()))]
metro_choices.extend(['All'])
dbutils.widgets.dropdown(name = 'METRO', defaultValue='All', choices = metro_choices, label = 'Select Metro')
if (region == dbutils.widgets.get('REGION') and metro == 'All' and zone == 'All'):
display(mainDF.filter(col('REGION').isin(region)))
elif (metro == dbutils.widgets.get('METRO') and zone == 'All'):
display(mainDF.filter(col('REGION').isin(region) & col('METRO').isin(metro)))
else :
display(mainDF.filter(col('REGION').isin(region) & col('METRO').isin(metro) & col('ZONE').isin(zone)))
initially if i run the dashboard for selected region say 'WEST' it runs fine and even i can select related metro and zone. But if I change region to say 'NORTH' I want metro and zone to auto set for 'All'.

Related

Intercept QTabWidget tab change [duplicate]

I am trying to make the user not switch to the next TAB where "Form 2" is located until they fill in Form 1.
I tried the "currentChange" event but it doesn't work the way I want as it shows the alert when it was already changed from TAB.
Is there a way to leave the current TAB fixed until the task is complete?
I attach the code and an image
import sys
from PyQt5.QtCore import Qt
from PyQt5 import QtWidgets
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.setGeometry(0, 0, 800, 500)
self.setLayout(QtWidgets.QVBoxLayout())
#flag to not show the alert when starting the program
self.flag = True
#changes to True when the form is completed
self.form_completed = False
#WIDGET TAB 1
self.widget_form1 = QtWidgets.QWidget()
self.widget_form1.setLayout(QtWidgets.QVBoxLayout())
self.widget_form1.layout().setAlignment(Qt.AlignHCenter)
label_form1 = QtWidgets.QLabel("FORM 1")
self.widget_form1.layout().addWidget(label_form1)
#WIDGET TAB 2
self.widget_form2 = QtWidgets.QWidget()
self.widget_form2.setLayout(QtWidgets.QVBoxLayout())
self.widget_form2.layout().setAlignment(Qt.AlignHCenter)
label_form2 = QtWidgets.QLabel("FORM 2")
self.widget_form2.layout().addWidget(label_form2)
#QTABWIDGET
self.tab_widget = QtWidgets.QTabWidget()
self.tab_widget.currentChanged.connect(self.changed)
self.tab_widget.addTab(self.widget_form1,"Form 1")
self.tab_widget.addTab(self.widget_form2, "Form 2")
self.layout().addWidget(self.tab_widget)
def changed(self,index):
if self.flag:
self.flag = False
return
if not self.form_completed:
QtWidgets.QMessageBox.about(self, "Warning", "You must complete the form")
return
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mw = MyWidget()
mw.show()
sys.exit(app.exec_())
The currentChanged signal is emitted when the index is already changed (the verb is in past tense: Changed), so if you want to prevent the change, you have to detect any user attempt to switch tab.
In order to do so, you must check both mouse and keyboard events:
left mouse clicks on the tab bar;
Ctrl+Tab and Ctrl+Shift+Tab on the tab widget;
Since you have to control that behavior from the main window, the only solution is to install an event filter on both the tab widget and its tabBar(), then if the action would change the index but the form is not completed, you must return True so that the event won't be handled by the widget.
Please consider that the following assumes that the tab that has to be kept active is the current (the first added tab, or the one set using setCurrentIndex()).
class MyWidget(QtWidgets.QWidget):
def __init__(self):
# ...
self.tab_widget.installEventFilter(self)
self.tab_widget.tabBar().installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == event.KeyPress and \
event.key() in (Qt.Key_Left, Qt.Key_Right):
return not self.form_completed
elif source == self.tab_widget.tabBar() and \
event.type() == event.MouseButtonPress and \
event.button() == Qt.LeftButton:
tab = self.tab_widget.tabBar().tabAt(event.pos())
if tab >= 0 and tab != self.tab_widget.currentIndex():
return self.isInvalid()
elif source == self.tab_widget and \
event.type() == event.KeyPress and \
event.key() in (Qt.Key_Tab, Qt.Key_Backtab) and \
event.modifiers() & Qt.ControlModifier:
return self.isInvalid()
return super().eventFilter(source, event)
def isInvalid(self):
if not self.form_completed:
QTimer.singleShot(0, lambda: QtWidgets.QMessageBox.about(
self, "Warning", "You must complete the form"))
return True
return False
Note that I showed the message box using a QTimer in order to properly return the event filter immediately.
Also consider that it's good practice to connect signals at the end of an object creation and configuration, and this is much more important for signals that notify property changes: you should not connect it before setting the property that could trigger it.
Since an empty QTabWidget has a -1 index, as soon as you add the first tab the index is changed to 0, thus triggering the signal. Just move the currentChanged signal connection after adding the tabs, and you can get rid of the self.flag check.

How to get a drop-down filter in Spotfire Information Link?

Generally people use the default option that Spotfire gives. Connect to the DB and pull the set of columns that you need and create an Information Link and load the data to Spotfire.
However, I am using SQL Query to fetch data to Spotfire. I am creating a table similar to Views, and writing a simple stored procedure to pull the data:
Create procedure ProcA(In Start_Date date, IN End_Date date, In Site_Name text)
Begin
SELECT * FROM TableA where day between Start_Date and End_Date and
site_name = Site_Name;
This works fine if I am not using site name filtering.
The Information Links helps in filtering the date properly. But when it comes to Site Name, nothing works.
There are 2 requirements:
Is it possible to give a drop-down just like how filter comes for Date
How to pass multiple site names to pull only those sites into the Spotfire file
TL;DR: There are better ways to do this; if it's just for the column names, I don't think it's worth it to do part 2, since it's easy enough to change the sql in the information link, but it's possible.
Okay, I will try (read: fail) not to be too long-winded.
1) Is it possible to do a drop-down for dates? Yes. The easiest way to do this would be to pull a data table with all of your date choices available for the end user. Here's an example finding a list of better way to generate months/year table Remember when creating your dropdownlist that your Document Property has to have the Data type "Date", and then you should be able to set property values through Unique Values in column against your date column from the new data, the same as you would do for a string drop-down list.
If you have a small subset of specific dates to choose from, this probably isn't too bad. If the drop down list gets longer, your end-users can type in the date they're looking for to speed up their search (though in my experience, a lot of them will scroll through until they find the date they're looking for).
While this is perfectly acceptable, if you're at all comfortable adding javascript, I'd personally recommend using a Popup Calendar These are fairly straightforward for end-users, and can allow them to use the calendar or type it themselves. (And if they type something that isn't a date in, it's even kind enough to inform them with red letters and an exclamation mark that they haven't typed an actual date)
2) How to pass multiple site names to pull only those sites into the Spotfire file
Hoo boi, where to start.
Step one: How do you want to select your list of Site Names? I'm going to go ahead and assume you have a data table with a list of distinct Site Names.
Your next choice is how to let your user select which Site Names they want. General options are using a List Box Filter, displaying a table and using marked rows, or providing a text area where the user can type their selections themselves.
When I needed to do this, I did a combo of a data table and a text area, so that's what I'm going to describe here.
I start off by providing the user with a text area, formatted to "specific size" with a larger than usual height to prompt that, yes, they are allowed to type multiple rows. If they know the values they're looking for, they can type them in manually, or copy paste from an excel file, etc.
If they don't know what they're looking for, the list of Site Names would be in a Table displayed for the user, where they can then mark the rows they want on the visualization and push a button which will do a cursor through the list of marked Site Names, concatenate them together, and put them in the text box previously mentioned (Note: if you don't want to let them enter their list manually, you can leave off the text area, combine these next two pieces of code, and throw it straight into the SpecialFilterProperty).
Please note that cursors are slow; if you have more than a few thousand rows to cycle through, this may stall out for a few seconds.
Code for the button:
from Spotfire.Dxp.Application.Visuals import CrossTablePlot
from Spotfire.Dxp.Data import IndexSet
from Spotfire.Dxp.Data import RowSelection
from Spotfire.Dxp.Data import DataValueCursor
from Spotfire.Dxp.Data import DataSelection
TextFltr = ""
crossSource = Document.Data.Tables["Distinct_SiteNames"]
##Get a Row Count
rowCount = Document.Data.Tables["Distinct_SiteNames"].RowCount
##Index Set of all our rows
rowIndexSet=Document.ActiveMarkingSelectionReference.GetSelection(Document.Data.Tables["Distinct_SiteNames"]).AsIndexSet()
allRows = IndexSet(rowCount,True)
if rowIndexSet.IsEmpty != True:
allRows = rowIndexSet
colCurs = DataValueCursor.CreateFormatted(crossSource.Columns["Site_Name"])
##Optional: Loop through to determine average value
colTotal = ''
for row in crossSource.GetRows(allRows, colCurs):
colTotal += ', ' + colCurs.CurrentValue
if TextFltr == "":
TextFltr += colTotal[2:]
else:
TextFltr += colTotal
Document.Properties["SelectedSiteNames"] = TextFltr
from System.Collections.Generic import Dictionary
from Spotfire.Dxp.Application.Scripting import ScriptDefinition
import clr
scriptDef = clr.Reference[ScriptDefinition]()
Document.ScriptManager.TryGetScript("Change Special Filter Value", scriptDef)
params = Dictionary[str, object]()
Document.ScriptManager.ExecuteScript(scriptDef.ScriptCode, params)
At the bottom it references a second script; this is the script attached to the button that parses through the text area when the user wants to submit their selections and refresh the data table.
The General Code I've used is here, script titled "Change Special Filter Value", which allows delimiting by newline, tabs, commas, quotes, and a few others. Feel free to add or subtract here, depending on your user-base's needs.
strVals = Document.Properties["SelectedSiteNames"]
lst = ""
cnt = 0
x = 0
y = 0
z = 0
for letter in strVals:
if y == 1:
if letter == " ":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == ",":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == "\n":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == "\r":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == "'":
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == '"':
lst = lst + "'" + strVals[x:z] + "', "
y = 0
elif letter == '\t':
lst = lst + "'" + strVals[x:z] + "', "
y = 0
else:
if letter <> " " and letter <> "," and letter <> "\n" and letter <> "\r" and letter <> "'" and letter <> '"' and letter <> "\t":
if y == 0:
cnt += 1
print letter
x = z
y = 1
z += 1
if y == 1:
lst = lst + "'" + strVals[x:z] + "', "
print lst
lst = lst.upper()
if len(lst) > 0:
lst = lst[1:len(lst) - 3]
Document.Properties["SpecialFilterValue"] = lst
Step one is now complete! You have a list of all your selected site names in a property that you can now pass to your stored procedure.
Note: I believe there's a limit to the number of characters Spotfire can pass through a string value. In my previous testing, I think it's been over 500,000 characters (it's been a while, so I don't remember exactly), so you have a lot of leeway, but it does exist, and depending on which data source you're using, it may be lower.
Step Two: Alter the stored Procedure
Your stored procedure will basically be something along the lines of this:
Create procedure ProcA(In Start_Date date, IN End_Date date, In Site_Name text)
Begin
DECLARE #Script nvarchar(max) =
N'
Select * from TableA where day between Start_Date and End_Date and Site_Name in (' + #Site_Name + ') '
EXECUTE (#Script)
Downright easy in comparison!
(No loop after all! The bizarre use case I was remembering doesn't apply here, unless you're also using a data base that doesn't allow you to pass parameters directly...)

Find node id by name in Umbraco Razor

I have a site with structure like Home -> News -> Year -> Month -> Day -> Article and need a query to find the id of the Day folder where the name of the folder matches a specified Year, Month and Day.
For example for 12th May 2003 it would be something like:
#something().where("Year.name = 2003").where("Month.name = May").where("Day.name = 12")
Depending on your preference, you can construct a query using either the dynamic DynamicPublishedContent or the strongly-typed IPublishedContent API.
The query will be different depending on where it's used, so assuming you want to run this query and display the results in the "News" page, you need something like the following:
var year = CurrentPage.Years.Where("Name == #1", "2013").First();
var month = year.Months.Where("Name == #1", "May").First();
var day = month.Days.Where("Name == #1", "12").First();
var dayId = day.Id;
Alternatively, you can try the strongly typed version:
var day = Model.Content
.Descendants("Year").First(y => y.Name == "2013")
.Descendants("Month").First(m => m.Name == "May")
.Descendants("Day").First(d => d.Name == "12").Id;
I've used First() for brevity but in reality you might want to check if the page exists before getting its descendants. Also, you might want to have a look at the documentation and come up with a query that's better suited to your scenario.

Hide Detail Field Depending other variable in Access Report View (Not Print View)

I have a tabbed control (TabCtl45) in the "Detail" section of my report that that has three tabs. (Page1, Page2, Page3). I only want to show one page depending on another variable known as Fee Type. If FeeType = 1, then Page1 tab shows. If FeeType = 2, then page Page2 tab shows... etc.
I was able to make this work in "Print View" with the OnFormat event; however, I want all my data in one easy flowing page like report view. Not in separate pages like print view outputs.
The code looked like this:
If Me.FeeType = "1" Then
Me.TabCtl45.Pages("Page1").Visible = True
Me.TabCtl45.Pages("Page2").Visible = False
Me.TabCtl45.Pages("Page3").Visible = False
ElseIf Me.FeeType = "2" Then
Me.TabCtl45.Pages("Page1").Visible = False
Me.TabCtl45.Pages("Page2").Visible = True
Me.TabCtl45.Pages("Page3").Visible = False
ElseIf Me.FeeType = "3" Then
Me.TabCtl45.Pages("Page1").Visible = False
Me.TabCtl45.Pages("Page2").Visible = False
Me.TabCtl45.Pages("Page3").Visible = True
End If
How do you I get this code to work in report view from the "Detail" section of my report? There is no event that I can see working for this purpose in the properties "Detail" section.
You can't. The Report View is rather limited, the section events (like Detail.OnFormat) simply don't run in this view. Only in Print/Print Preview.
The only thing you can do to format single records in Report View is Conditional Formatting, but that's no use to hide things.
On an unrelated note, you can simplify your code a lot by doing:
Me.TabCtl45.Pages("Page1").Visible = (Me.FeeType = "1")
Me.TabCtl45.Pages("Page2").Visible = (Me.FeeType = "2")
Me.TabCtl45.Pages("Page3").Visible = (Me.FeeType = "3")

If Null then Clear if not then leave alone

In my form I have a search function setup to where I can type in any of the fields and search for that particular data, the course fields though need to be CLEARED if I have not preselected something for their fields. I have their default values at null. Currently the code looks like
Private Function SearchClear()
Me.cboDevelopment1 = ""
Me.cboDevelopment2 = ""
Me.cboDevelopment3 = ""
Me.cboDevelopment4 = ""
Me.cboDevelopment5 = ""
'focus on ID text box
Me.txtEmpID.SetFocus
'set button edit to enable
Me.cmdEdit.Enabled = True
'change caption of button add to Add
Me.cmdAdd.Caption = "Add Record"
'clear tag on txt id for reset new
Me.txtEmpID.Tag = ""
End Function
I would like to write in saying IF NULL then to clear the fields, but if not then let them be so the designated courses can be looked up.
I even tried adding in And Is Not Null to the search query that is run in the background but that did not work.
So you have a form for searching for courses.
You say you have set the defaults to null so would it look like this?
Me.cboDevelopment1 = NULL
Me.cboDevelopment2 = NULL
Me.cboDevelopment3 = NULL
Me.cboDevelopment4 = NULL
Me.cboDevelopment5 = NULL
Or, when you say they have to be cleared if they don't have something preselected the users has selected a value and now your form looks like this before you run your search?
Me.cboDevelopment1 = NULL
Me.cboDevelopment2 = VALUE1
Me.cboDevelopment3 = NULL
Me.cboDevelopment4 = VALUE2
Me.cboDevelopment5 = NULL
And then the user would click search and you would perform this CLEAR you are looking for before searching?